diff --git a/src/elements/HDLAssignment.ts b/src/elements/HDLAssignment.ts new file mode 100644 index 0000000..9f461e9 --- /dev/null +++ b/src/elements/HDLAssignment.ts @@ -0,0 +1,27 @@ +import { HDLElement } from './HDLElement'; +import { + IHDLAssignment, + IHDLAssignmentInfo, +} from './interfaces/IHDLAssignment'; + +/** + * Concurrent signal assignment in VHDL. + */ +export class HDLAssignment + extends HDLElement + implements IHDLAssignment +{ + /** + * @inheritdoc + */ + get target(): string { + return this._info.target; + } + + /** + * @inheritdoc + */ + get expression(): string { + return this._info.expression; + } +} diff --git a/src/elements/HDLConstant.ts b/src/elements/HDLConstant.ts new file mode 100644 index 0000000..6be0825 --- /dev/null +++ b/src/elements/HDLConstant.ts @@ -0,0 +1,24 @@ +import { HDLElement } from './HDLElement'; +import { IHDLConstantInfo, IHDLConstant } from './interfaces/IHDLConstant'; + +/** + * Constant element in VHDL. + */ +export class HDLConstant + extends HDLElement + implements IHDLConstant +{ + /** + * @inheritdoc + */ + get type(): string { + return this._info.type; + } + + /** + * @inheritdoc + */ + get defaultValue(): string | undefined { + return this._info.defaultValue; + } +} diff --git a/src/elements/HDLFunction.ts b/src/elements/HDLFunction.ts index 06dca84..189335f 100644 --- a/src/elements/HDLFunction.ts +++ b/src/elements/HDLFunction.ts @@ -1,4 +1,5 @@ import { HDLElement } from './HDLElement'; +import { IHDLParameter } from './interfaces/Common'; import { IHDLFunctionInfo, IHDLFunction } from './interfaces/IHDLFunction'; /** @@ -15,6 +16,13 @@ export class HDLFunction return this._info.parameters; } + /** + * @inheritdoc + */ + get parameterList(): IHDLParameter[] { + return this._info.parameterList; + } + /** * @inheritdoc */ diff --git a/src/elements/HDLGeneric.ts b/src/elements/HDLGeneric.ts new file mode 100644 index 0000000..8ba0eba --- /dev/null +++ b/src/elements/HDLGeneric.ts @@ -0,0 +1,18 @@ +import { HDLElement } from './HDLElement'; +import { IHDLParameter } from './interfaces/Common'; +import { IHDLGenericInfo, IHDLGeneric } from './interfaces/IHDLGeneric'; + +/** + * Generic element in VHDL. + */ +export class HDLGeneric + extends HDLElement + implements IHDLGeneric +{ + /** + * @inheritdoc + */ + get genericList(): IHDLParameter[] | undefined { + return this._info.genericList; + } +} diff --git a/src/elements/HDLPort.ts b/src/elements/HDLPort.ts new file mode 100644 index 0000000..8f08eaa --- /dev/null +++ b/src/elements/HDLPort.ts @@ -0,0 +1,22 @@ +import { HDLElement } from './HDLElement'; +import { IHDLParameter, IVirtualBus } from './interfaces/Common'; +import { IHDLPortInfo, IHDLPort } from './interfaces/IHDLPort'; + +/** + * Port element for HDL + */ +export class HDLPort extends HDLElement implements IHDLPort { + /** + * @inheritdoc + */ + get portList(): IHDLParameter[] | undefined { + return this._info.portList; + } + + /** + * @inheritdoc + */ + get virtualBusList(): IVirtualBus[] | undefined { + return this._info.virtualBusList; + } +} diff --git a/src/elements/HDLProcedure.ts b/src/elements/HDLProcedure.ts index 73d2ef0..ab008e1 100644 --- a/src/elements/HDLProcedure.ts +++ b/src/elements/HDLProcedure.ts @@ -1,4 +1,5 @@ import { HDLElement } from './HDLElement'; +import { IHDLParameter } from './interfaces/Common'; import { IHDLProcedureInfo, IHDLProcedure } from './interfaces/IHDLProcedure'; /** @@ -14,4 +15,11 @@ export class HDLProcedure get parameters(): string { return this._info.parameters; } + + /** + * @inheritdoc + */ + get parameterList(): IHDLParameter[] { + return this._info.parameterList; + } } diff --git a/src/elements/HDLType.ts b/src/elements/HDLType.ts new file mode 100644 index 0000000..885f8fd --- /dev/null +++ b/src/elements/HDLType.ts @@ -0,0 +1,14 @@ +import { HDLElement } from './HDLElement'; +import { IHDLType, IHDLTypeInfo } from './interfaces/IHDLTypeInfo'; + +/** + * Type element in VHDL. + */ +export class HDLType extends HDLElement implements IHDLType { + /** + * @inheritdoc + */ + get typeExpression(): string { + return this._info.typeExpression; + } +} diff --git a/src/elements/HDLVariable.ts b/src/elements/HDLVariable.ts new file mode 100644 index 0000000..9fbe51f --- /dev/null +++ b/src/elements/HDLVariable.ts @@ -0,0 +1,24 @@ +import { HDLElement } from './HDLElement'; +import { IHDLVariableInfo, IHDLVariable } from './interfaces/IHDLVariable'; + +/** + * Variable element in VHDL. + */ +export class HDLVariable + extends HDLElement + implements IHDLVariable +{ + /** + * @inheritdoc + */ + get type(): string { + return this._info.type; + } + + /** + * @inheritdoc + */ + get defaultValue(): string | undefined { + return this._info.defaultValue; + } +} diff --git a/src/elements/interfaces/Common.ts b/src/elements/interfaces/Common.ts index d51b0b1..b428199 100644 --- a/src/elements/interfaces/Common.ts +++ b/src/elements/interfaces/Common.ts @@ -50,3 +50,58 @@ export interface IPosition { */ endColumn: number; } + +/** + * Interface for HDL parameters/ports/generics. + */ +export interface IHDLParameter { + /** + * The name of the parameter. + */ + name: string; + + /** + * The mode of the parameter. + * e.g., "in", "out", "inout" + */ + mode?: string; + + /** + * The type of the parameter. + * e.g., "std_logic", "integer", "real" + */ + type: string; + + /** + * Default value, if specified. + */ + defaultValue?: string; + + /** + * Optional comment describing the generic. + */ + comment?: string; +} + +export interface IVirtualBus { + /** + * The name of the virtual bus. + */ + name: string; + + /** + * The mode of the virtual bus. + * e.g., "in", "out", "inout" + */ + mode?: string; + + /** + * The signals of the virtual bus. + */ + signals: IHDLParameter[]; + + /** + * Optional comment describing the virtual bus. + */ + comment?: string; +} diff --git a/src/elements/interfaces/IHDLAssignment.ts b/src/elements/interfaces/IHDLAssignment.ts new file mode 100644 index 0000000..832b7b3 --- /dev/null +++ b/src/elements/interfaces/IHDLAssignment.ts @@ -0,0 +1,20 @@ +import { IHDLElement, IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for concurrent signal assignment metadata. + */ +export interface IHDLAssignmentInfo extends IHDLElementInfo { + /** Name of the signal being assigned. */ + target: string; + + /** The expression assigned to the signal. */ + expression: string; +} + +/** + * Interface for concurrent signal assignment elements. + */ +export interface IHDLAssignment extends IHDLElement { + get target(): string; + get expression(): string; +} diff --git a/src/elements/interfaces/IHDLConstant.ts b/src/elements/interfaces/IHDLConstant.ts new file mode 100644 index 0000000..cf686c7 --- /dev/null +++ b/src/elements/interfaces/IHDLConstant.ts @@ -0,0 +1,32 @@ +import { IHDLElement, IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for constant metadata. + */ +export interface IHDLConstantInfo extends IHDLElementInfo { + /** + * Type of the constant. + * e.g. "integer", "std_logic". + */ + type: string; + + /** + * Default value of the constant. + */ + defaultValue?: string; +} + +/** + * Interface for constant elements. + */ +export interface IHDLConstant extends IHDLElement { + /** + * Get the type of the constant. + */ + get type(): string; + + /** + * Get the default value of the constant. + */ + get defaultValue(): string | undefined; +} diff --git a/src/elements/interfaces/IHDLFunction.ts b/src/elements/interfaces/IHDLFunction.ts index 0ce7a2d..72a77ac 100644 --- a/src/elements/interfaces/IHDLFunction.ts +++ b/src/elements/interfaces/IHDLFunction.ts @@ -1,3 +1,4 @@ +import { IHDLParameter } from './Common'; import { IHDLElement, IHDLElementInfo } from './IHDLElement'; /** @@ -9,6 +10,11 @@ export interface IHDLFunctionInfo extends IHDLElementInfo { */ parameters: string; + /** + * Structured list of parameters. + */ + parameterList: IHDLParameter[]; + /** * Return type. */ @@ -24,6 +30,11 @@ export interface IHDLFunction extends IHDLElement { */ get parameters(): string; + /** + * Get structured parameter list. + */ + get parameterList(): IHDLParameter[]; + /** * Get the return type of the function. */ diff --git a/src/elements/interfaces/IHDLGeneric.ts b/src/elements/interfaces/IHDLGeneric.ts new file mode 100644 index 0000000..78e33cc --- /dev/null +++ b/src/elements/interfaces/IHDLGeneric.ts @@ -0,0 +1,22 @@ +import { IHDLParameter } from './Common'; +import { IHDLElement, IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for process metadata. + */ +export interface IHDLGenericInfo extends IHDLElementInfo { + /** + * List of generics. + */ + genericList?: IHDLParameter[]; +} + +/** + * Interface for process elements. + */ +export interface IHDLGeneric extends IHDLElement { + /** + * Get the generic list of the element. + */ + get genericList(): IHDLParameter[] | undefined; +} diff --git a/src/elements/interfaces/IHDLPort.ts b/src/elements/interfaces/IHDLPort.ts new file mode 100644 index 0000000..f46d5c7 --- /dev/null +++ b/src/elements/interfaces/IHDLPort.ts @@ -0,0 +1,32 @@ +import { IHDLParameter, IVirtualBus } from './Common'; +import { IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for port metadata. + */ +export interface IHDLPortInfo extends IHDLElementInfo { + /** + * List of ports. + */ + portList?: IHDLParameter[]; + + /** + * List of virtual buses. + */ + virtualBusList?: IVirtualBus[]; +} + +/** + * Interface for port elements. + */ +export interface IHDLPort { + /** + * Get the port list of the element. + */ + get portList(): IHDLParameter[] | undefined; + + /** + * Get the virtual bus list of the element. + */ + get virtualBusList(): IVirtualBus[] | undefined; +} diff --git a/src/elements/interfaces/IHDLProcedure.ts b/src/elements/interfaces/IHDLProcedure.ts index 986b038..851c7d7 100644 --- a/src/elements/interfaces/IHDLProcedure.ts +++ b/src/elements/interfaces/IHDLProcedure.ts @@ -1,3 +1,4 @@ +import { IHDLParameter } from './Common'; import { IHDLElement, IHDLElementInfo } from './IHDLElement'; /** @@ -8,6 +9,11 @@ export interface IHDLProcedureInfo extends IHDLElementInfo { * Parameter list as string. */ parameters: string; + + /** + * Parameter list as structured array. + */ + parameterList: IHDLParameter[]; } /** @@ -18,4 +24,9 @@ export interface IHDLProcedure extends IHDLElement { * Get the parameter list of the procedure. */ get parameters(): string; + + /** + * Get the parameter list of the procedure as structured array. + */ + get parameterList(): IHDLParameter[]; } diff --git a/src/elements/interfaces/IHDLTypeInfo.ts b/src/elements/interfaces/IHDLTypeInfo.ts new file mode 100644 index 0000000..7fa717e --- /dev/null +++ b/src/elements/interfaces/IHDLTypeInfo.ts @@ -0,0 +1,21 @@ +import { IHDLElement, IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for type metadata. + */ +export interface IHDLTypeInfo extends IHDLElementInfo { + /** + * Full VHDL array/type expression. + */ + typeExpression: string; +} + +/** + * Interface for type elements. + */ +export interface IHDLType extends IHDLElement { + /** + * Get the full VHDL type expression. + */ + get typeExpression(): string; +} diff --git a/src/elements/interfaces/IHDLVariable.ts b/src/elements/interfaces/IHDLVariable.ts new file mode 100644 index 0000000..a4934ea --- /dev/null +++ b/src/elements/interfaces/IHDLVariable.ts @@ -0,0 +1,32 @@ +import { IHDLElement, IHDLElementInfo } from './IHDLElement'; + +/** + * Interface for variable metadata. + */ +export interface IHDLVariableInfo extends IHDLElementInfo { + /** + * Type of the variable. + * e.g. "integer", "std_logic". + */ + type: string; + + /** + * Default value of the variable. + */ + defaultValue?: string; +} + +/** + * Interface for variable elements. + */ +export interface IHDLVariable extends IHDLElement { + /** + * Get the type of the variable. + */ + get type(): string; + + /** + * Get the default value of the variable. + */ + get defaultValue(): string | undefined; +} diff --git a/src/extractors/AssignmentExtractor.ts b/src/extractors/AssignmentExtractor.ts new file mode 100644 index 0000000..c3ac0f0 --- /dev/null +++ b/src/extractors/AssignmentExtractor.ts @@ -0,0 +1,56 @@ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLAssignment } from '../elements/HDLAssignment'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLAssignmentInfo } from '../elements/interfaces/IHDLAssignment'; + +/** + * AssignmentExtractor extracts concurrent signal assignments from a VHDL AST. + */ +export class AssignmentExtractor implements INodeExtractor { + public readonly nodeType = 'simple_concurrent_signal_assignment'; + + /** + * @inheritdoc + */ + public readonly excludedParents = ['process_statement', 'declarative_part']; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLAssignment[] => { + const result: HDLAssignment[] = []; + + const targetNode = node.childForFieldName('target'); + + const waveformsNode = node.namedChildren.find( + (c) => c.type === 'waveforms', + ); + + const target = targetNode?.text.trim() ?? 'unknown'; + const expression = waveformsNode?.text.trim() ?? 'unknown'; + + const pos = Position.fromNode(node); + const comment = getLeadingComment(pos.startLine); + + const info: IHDLAssignmentInfo = { + name: target, + target, + expression, + comment, + description: undefined, + position: pos, + }; + + result.push(new HDLAssignment(info)); + + return result; + }; +} diff --git a/src/extractors/ConstantExtractor.ts b/src/extractors/ConstantExtractor.ts new file mode 100644 index 0000000..9ab0484 --- /dev/null +++ b/src/extractors/ConstantExtractor.ts @@ -0,0 +1,66 @@ +/* eslint-disable max-len */ +/* eslint-disable no-console */ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLConstant } from '../elements/HDLConstant'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLConstantInfo } from '../elements/interfaces/IHDLConstant'; + +/** + * ConstantExtractor extracts constant declarations from a VHDL AST. + */ +export class ConstantExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public readonly nodeType = 'constant_declaration'; + + /** + * @inheritdoc + */ + public readonly excludedParents = [ + 'function_body', + 'procedure_body', + 'process_statement', + ]; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLConstant[] => { + const result: HDLConstant[] = []; + + const nameNode = node.descendantsOfType('identifier_list')[0]; + const typeNode = node.descendantsOfType('subtype_indication')[0]; + const defaultNode = node.descendantsOfType('default_expression')[0]; + + const names = nameNode.text.split(',').map((s) => s.trim()); + const type = typeNode?.text ?? ''; + const defaultValue = defaultNode?.text ?? undefined; + const pos = Position.fromNode(nameNode); + + for (const name of names) { + const comment = getLeadingComment(pos.startLine); + + const info: IHDLConstantInfo = { + name, + type, + defaultValue, + comment, + description: undefined, + position: pos, + }; + + result.push(new HDLConstant(info)); + } + + return result; + }; +} diff --git a/src/extractors/FunctionExtractor.ts b/src/extractors/FunctionExtractor.ts new file mode 100644 index 0000000..4240686 --- /dev/null +++ b/src/extractors/FunctionExtractor.ts @@ -0,0 +1,103 @@ +import { IHDLParameter } from 'elements/interfaces/Common'; +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLFunction } from '../elements/HDLFunction'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLFunctionInfo } from '../elements/interfaces/IHDLFunction'; + +/** + * FunctionExtractor extracts function declarations from a VHDL AST. + */ +export class FunctionExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public readonly nodeType = 'function_body'; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLFunction[] => { + const result: HDLFunction[] = []; + + const nameNode = node.namedChildren.find( + (c) => c.type === 'identifier', + ); + + const parameterClause = node.namedChildren.find( + (c) => c.type === 'function_parameter_clause', + ); + + const returnNode = node.namedChildren.find((c) => c.type === 'return'); + + const returnTypeNode = returnNode?.namedChildren.find( + (c) => c.type === 'type_mark', + ); + + const name = nameNode?.text.trim() ?? 'unnamed'; + const returnType = returnTypeNode?.text.trim() ?? 'void'; + + const paramLines: string[] = []; + const parameterList: IHDLParameter[] = []; + + if (parameterClause) { + for (const decl of parameterClause.namedChildren) { + const identifierList = decl.namedChildren + .find((c) => c.type === 'identifier_list') + ?.text.trim(); + + const typeNode = decl.namedChildren.find( + (c) => c.type === 'subtype_indication', + ); + + const modeNode = decl.namedChildren.find( + (c) => c.type === 'mode', + ); + + const mode = modeNode?.text.trim(); // optional! + const type = typeNode?.text.trim() ?? ''; + + if (identifierList) { + const names = identifierList + .split(',') + .map((n) => n.trim()); + + for (const name of names) { + parameterList.push({ name, mode, type }); + + paramLines.push( + mode + ? `${name}: ${mode} ${type}` + : `${name}: ${type}`, + ); + } + } + } + } + + const parameters = paramLines.join('; '); + const pos = Position.fromNode(node); + const comment = getLeadingComment(pos.startLine); + + const info: IHDLFunctionInfo = { + name, + parameters, + parameterList, + returnType, + position: pos, + comment, + description: undefined, + }; + + result.push(new HDLFunction(info)); + + return result; + }; +} diff --git a/src/extractors/GenericExtractor.ts b/src/extractors/GenericExtractor.ts new file mode 100644 index 0000000..dbf44cf --- /dev/null +++ b/src/extractors/GenericExtractor.ts @@ -0,0 +1,90 @@ +import { Position } from 'elements/Common'; +import { HDLGeneric } from 'elements/HDLGeneric'; +import { IHDLParameter } from 'elements/interfaces/Common'; +import { IHDLGenericInfo } from 'elements/interfaces/IHDLGeneric'; +import Parser from 'web-tree-sitter'; +import { + INodeExtractor, + NodeHandler, + GetLeadingComment, +} from './interfaces/IHDLElementExtractor'; + +/** + * GenericExtractor extracts generic declarations from a VHDL AST. + */ +export class GenericExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public get nodeType(): string { + return 'generic_clause'; + } + + /** + * @inheritdoc + */ + public readonly excludedParents = [ + 'function_body', + 'procedure_body', + 'process_statement', + ]; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLGeneric[] => { + const result: HDLGeneric[] = []; + + const genericNodes = node.descendantsOfType( + 'constant_interface_declaration', + ); + + const generics: IHDLParameter[] = []; + + for (const genericNode of genericNodes) { + const nameNode = + genericNode.childForFieldName('identifier') ?? + genericNode.descendantsOfType('identifier')[0]; + + const typeNode = + genericNode.childForFieldName('subtype_indication') ?? + genericNode.descendantsOfType('subtype_indication')[0]; + + const defaultNode = + genericNode.childForFieldName('default_expression') ?? + genericNode.descendantsOfType('default_expression')[0]; + + const name = nameNode?.text ?? ''; + const type = typeNode?.text ?? ''; + const defaultValue = defaultNode?.text ?? undefined; + + const comment = getLeadingComment( + nameNode?.startPosition.row ?? genericNode.startPosition.row, + ); + + generics.push({ + name, + type, + defaultValue, + comment, + }); + } + + const pos = Position.fromNode(node); + + const info: IHDLGenericInfo = { + name: 'GenericClause', + genericList: generics, + position: pos, + comment: getLeadingComment(pos.startLine), + description: undefined, + }; + + result.push(new HDLGeneric(info)); + + return result; + }; +} diff --git a/src/extractors/HDLElementExtractor.ts b/src/extractors/HDLElementExtractor.ts index ae264f8..a3aad25 100644 --- a/src/extractors/HDLElementExtractor.ts +++ b/src/extractors/HDLElementExtractor.ts @@ -1,17 +1,21 @@ import Parser from 'web-tree-sitter'; import { ICommentOptions } from './interfaces/ICommentOptions'; +import { + GetLeadingComment, + IHDLElementExtractor, + INodeExtractor, +} from './interfaces/IHDLElementExtractor'; import { IHDLElement, IHDLElementInfo, } from '../elements/interfaces/IHDLElement'; - /** - * Abstract base class for all HDL element extractors. + * Base class for all HDL element extractors. * Collects and provides access to leading comments and implements recursive AST traversal. */ -export abstract class HDLElementExtractor< - T extends IHDLElement, -> { +export class HDLElementExtractor> + implements IHDLElementExtractor +{ protected readonly _comments: Map = new Map(); constructor( @@ -20,6 +24,8 @@ export abstract class HDLElementExtractor< markerPrefix: '@', stripPrefix: true, }, + + protected readonly nodeExtractor: INodeExtractor, ) {} /** @@ -60,7 +66,9 @@ export abstract class HDLElementExtractor< * @param line The line number to check for comments. * @returns The comment block as a string, or undefined if no comments are found. */ - protected getLeadingComment(line: number): string | undefined { + private readonly getLeadingComment: GetLeadingComment = ( + line: number, + ): string | undefined => { const lines: string[] = []; let currentLine = line - 1; @@ -70,7 +78,7 @@ export abstract class HDLElementExtractor< } return lines.length > 0 ? lines.join('\n') : undefined; - } + }; /** * Recursively find all nodes of a given type in the syntax tree. @@ -84,6 +92,10 @@ export abstract class HDLElementExtractor< ): Parser.SyntaxNode[] { const result: Parser.SyntaxNode[] = []; + if (this.nodeExtractor.excludedParents?.includes(node.type)) { + return result; + } + if (node.type === type) { result.push(node); } @@ -96,7 +108,25 @@ export abstract class HDLElementExtractor< } /** - * The main extraction method to be implemented by subclasses. + * Extract all elements of the specified type from the syntax tree. + * Uses the provided handler function to process each node. + * @returns An array of extracted elements. */ - abstract extract(): T[]; + public extract(): T[] { + this.extractComments(); + const result: T[] = []; + + const nodes = this.findNodesByType( + this.root, + this.nodeExtractor.nodeType, + ); + + for (const node of nodes) { + result.push( + ...this.nodeExtractor.nodeHandler(node, this.getLeadingComment), + ); + } + + return result; + } } diff --git a/src/extractors/InstantiationExtractor.ts b/src/extractors/InstantiationExtractor.ts new file mode 100644 index 0000000..3bd3c28 --- /dev/null +++ b/src/extractors/InstantiationExtractor.ts @@ -0,0 +1,65 @@ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { Instantiation } from '../elements/HDLInstantiation'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLInstantiationInfo } from '../elements/interfaces/IHDLInstantiation'; + +/** + * InstantiationExtractor extracts component instantiations from a VHDL AST. + */ +export class InstantiationExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public get nodeType(): string { + return 'component_instantiation_statement'; + } + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): Instantiation[] => { + const result: Instantiation[] = []; + + // Name of the instance (label) + const labelNode = node.namedChildren.find((c) => c.type === 'label'); + + const identifier = labelNode?.namedChildren.find( + (c) => c.type === 'identifier', + ); + const name = identifier?.text.trim() ?? 'unnamed'; + + // Try to extract instantiated entity name + const entityInstantiation = node.namedChildren.find( + (c) => c.type === 'entity_instantiation', + ); + + const selectedName = entityInstantiation?.namedChildren.find( + (c) => c.type === 'selected_name', + ); + + const instanceType = selectedName?.text.trim() ?? 'unknown'; + + const pos = Position.fromNode(node); + const comment = getLeadingComment(pos.startLine); + + const info: IHDLInstantiationInfo = { + name, + instanceType, + position: pos, + comment, + description: undefined, + }; + + result.push(new Instantiation(info)); + + return result; + }; +} diff --git a/src/extractors/PortExtractor.ts b/src/extractors/PortExtractor.ts new file mode 100644 index 0000000..06c3209 --- /dev/null +++ b/src/extractors/PortExtractor.ts @@ -0,0 +1,122 @@ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLPort } from '../elements/HDLPort'; +import { + INodeExtractor, + NodeHandler, + GetLeadingComment, +} from './interfaces/IHDLElementExtractor'; +import { IHDLParameter, IVirtualBus } from '../elements/interfaces/Common'; +import { IHDLPortInfo } from '../elements/interfaces/IHDLPort'; + +/** + * PortExtractor extracts port declarations and virtual buses from a VHDL AST. + */ +export class PortExtractor implements INodeExtractor { + public readonly nodeType = 'port_clause'; + public readonly excludedParents = []; + + private extractText(node: Parser.SyntaxNode | null | undefined): string { + return node?.text?.trim() ?? ''; + } + + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLPort[] => { + const ports: IHDLParameter[] = []; + const virtualBuses: IVirtualBus[] = []; + + let currentVB: IVirtualBus | null = null; + let commentBuffer: string[] = []; + + for (const child of node.children) { + if (child.type === 'comment') { + const text = child.text.replace(/^--@\s*/, '').trim(); + + if (text.startsWith('@virtualbus')) { + const parts = text.split(/\s+/); + const name = parts[1]; + const dirIndex = parts.indexOf('@dir'); + + const dir = + dirIndex !== -1 ? parts[dirIndex + 1] : undefined; + const comment = parts.slice(dirIndex + 2).join(' '); + + currentVB = { + name, + mode: dir, + comment, + signals: [], + }; + + virtualBuses.push(currentVB); + commentBuffer = []; + continue; + } + + if (text.startsWith('@end')) { + currentVB = null; + commentBuffer = []; + continue; + } + + commentBuffer.push(text); + continue; + } + + if (child.type === 'signal_interface_declaration') { + const idNode = child.children.find( + (c) => c.type === 'identifier_list', + ); + const modeNode = child.children.find((c) => c.type === 'mode'); + + const typeNode = child.children.find( + (c) => c.type === 'subtype_indication', + ); + + const defaultNode = child.children.find( + (c) => c.type === 'default_expression', + ); + + const name = this.extractText( + idNode?.namedChildCount === 1 + ? idNode.namedChild(0) + : idNode, + ); + const mode = this.extractText(modeNode); + const type = this.extractText(typeNode); + const defaultValue = this.extractText(defaultNode); + + const paramComment = + commentBuffer.length > 0 + ? commentBuffer.join('\n') + : getLeadingComment(Position.fromNode(child).startLine); + + const param: IHDLParameter = { + name, + mode, + type, + defaultValue, + comment: paramComment, + }; + + if (currentVB) { + currentVB.signals.push(param); + commentBuffer = []; + } else { + ports.push(param); + } + } + } + + const info: IHDLPortInfo = { + name: '', + portList: ports, + virtualBusList: virtualBuses, + position: Position.fromNode(node), + }; + + return [new HDLPort(info)]; + }; +} diff --git a/src/extractors/ProcedureExtractor.ts b/src/extractors/ProcedureExtractor.ts new file mode 100644 index 0000000..c1cbec1 --- /dev/null +++ b/src/extractors/ProcedureExtractor.ts @@ -0,0 +1,86 @@ +import { IHDLParameter } from 'elements/interfaces/Common'; +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLProcedure } from '../elements/HDLProcedure'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLProcedureInfo } from '../elements/interfaces/IHDLProcedure'; + +/** + * ProcedureExtractor extracts procedure declarations from a VHDL AST. + */ +export class ProcedureExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public readonly nodeType = 'procedure_body'; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLProcedure[] => { + const result: HDLProcedure[] = []; + + const nameNode = node.namedChildren.find( + (c) => c.type === 'identifier', + ); + + const parameterClause = node.namedChildren.find( + (c) => c.type === 'procedure_parameter_clause', + ); + + const parameters: string[] = []; + const parameterList: IHDLParameter[] = []; + + if (parameterClause) { + for (const decl of parameterClause.namedChildren) { + const nameList = decl.namedChildren.find( + (c) => c.type === 'identifier_list', + ); + + const modeNode = decl.namedChildren.find( + (c) => c.type === 'mode', + ); + + const typeNode = decl.namedChildren.find( + (c) => c.type === 'subtype_indication', + ); + + const mode = modeNode?.text.trim() ?? ''; + const type = typeNode?.text.trim() ?? ''; + + if (nameList) { + const names = nameList.text.split(',').map((n) => n.trim()); + + for (const name of names) { + parameterList.push({ name, mode, type }); + parameters.push(`${name}: ${mode} ${type}`); + } + } + } + } + + const name = nameNode?.text.trim() ?? 'unnamed'; + const pos = Position.fromNode(node); + const comment = getLeadingComment(pos.startLine); + + const info: IHDLProcedureInfo = { + name, + parameters: parameters.join('; '), + parameterList, + position: pos, + comment, + description: undefined, + }; + + result.push(new HDLProcedure(info)); + + return result; + }; +} diff --git a/src/extractors/ProcessExtractor.ts b/src/extractors/ProcessExtractor.ts new file mode 100644 index 0000000..2de0a34 --- /dev/null +++ b/src/extractors/ProcessExtractor.ts @@ -0,0 +1,63 @@ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLProcess } from '../elements/HDLProcess'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLProcessInfo } from '../elements/interfaces/IHDLProcess'; + +/** + * ProcessExtractor is a class that extracts process declarations from a VHDL AST. + */ +export class ProcessExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public get nodeType(): string { + return 'process_statement'; + } + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLProcess[] => { + const result: HDLProcess[] = []; + + const labelNode = node.namedChildren.find((c) => c.type === 'label'); + + const identifier = labelNode?.namedChildren.find( + (c) => c.type === 'identifier', + ); + + const sensitivityListNode = node.namedChildren.find( + (c) => c.type === 'sensitivity_list', + ); + + const name = identifier?.text.trim() ?? 'unnamed'; + const pos = Position.fromNode(node); + + const sensitivityList = sensitivityListNode + ? sensitivityListNode.namedChildren.map((n) => n.text.trim()) + : undefined; + + const comment = getLeadingComment(pos.startLine); + + const info: IHDLProcessInfo = { + name, + position: pos, + comment, + description: undefined, + sensitivityList, + processType: undefined, + }; + + result.push(new HDLProcess(info)); + + return result; + }; +} diff --git a/src/extractors/SignalExtractor.ts b/src/extractors/SignalExtractor.ts index 091d113..58bb5b0 100644 --- a/src/extractors/SignalExtractor.ts +++ b/src/extractors/SignalExtractor.ts @@ -1,52 +1,66 @@ -import { HDLElementExtractor } from './HDLElementExtractor'; +import Parser from 'web-tree-sitter'; import { Position } from '../elements/Common'; import { HDLSignal } from '../elements/HDLSignal'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; import { IHDLSignalInfo } from '../elements/interfaces/IHDLSignal'; /** - * Extractor for VHDL signal declarations. + * SignalExtractor extracts signal declarations from a VHDL AST. */ -export class SignalExtractor extends HDLElementExtractor { +export class SignalExtractor implements INodeExtractor { /** - * Extract all signal declarations from the syntax tree. - * @returns An array of HDLSignal objects representing the extracted signals. + * @inheritdoc */ - extract(): HDLSignal[] { - this.extractComments(); + public get nodeType(): string { + return 'signal_declaration'; + } + /** + * @inheritdoc + */ + public readonly excludedParents = [ + 'function_body', + 'procedure_body', + 'process_statement', + ]; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLSignal[] => { const result: HDLSignal[] = []; - const signalNodes = this.findNodesByType( - this.root, - 'signal_declaration', - ); + const nameNode = node.descendantsOfType('identifier_list')[0]; + const typeNode = node.descendantsOfType('subtype_indication')[0]; + const defaultNode = node.descendantsOfType('default_expression')[0]; - for (const node of signalNodes) { - const nameNode = node.descendantsOfType('identifier_list')[0]; - const typeNode = node.descendantsOfType('subtype_indication')[0]; - const defaultNode = node.descendantsOfType('default_expression')[0]; + const names = nameNode.text.split(',').map((s) => s.trim()); + const type = typeNode?.text ?? ''; + const defaultValue = defaultNode?.text ?? undefined; + const pos = Position.fromNode(nameNode); - const names = nameNode.text.split(',').map((s) => s.trim()); - const type = typeNode?.text ?? ''; - const defaultValue = defaultNode?.text ?? undefined; - const pos = Position.fromNode(nameNode); + for (const name of names) { + const comment = getLeadingComment(pos.startLine); - for (const name of names) { - const comment = this.getLeadingComment(pos.startLine); + const info: IHDLSignalInfo = { + name, + type, + defaultValue, + comment, + description: undefined, + position: pos, + }; - const info: IHDLSignalInfo = { - name, - type, - defaultValue, - comment, - description: undefined, - position: pos, - }; - - result.push(new HDLSignal(info)); - } + result.push(new HDLSignal(info)); } return result; - } + }; } diff --git a/src/extractors/TypeExtractor.ts b/src/extractors/TypeExtractor.ts new file mode 100644 index 0000000..5a35105 --- /dev/null +++ b/src/extractors/TypeExtractor.ts @@ -0,0 +1,55 @@ +import { IHDLTypeInfo } from 'elements/interfaces/IHDLTypeInfo'; +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLType } from '../elements/HDLType'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; + +/** + * TypeExtractor extracts full type declarations from a VHDL AST. + */ +export class TypeExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public readonly nodeType = 'full_type_declaration'; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLType[] => { + const result: HDLType[] = []; + + const nameNode = node.namedChildren.find( + (c) => c.type === 'identifier', + ); + + const typeExprNode = node.namedChildren.find( + (c) => c.type === 'constrained_array_definition', + ); + + const name = nameNode?.text.trim() ?? 'unnamed_type'; + const typeExpression = typeExprNode?.text.trim() ?? 'unknown'; + + const pos = Position.fromNode(node); + const comment = getLeadingComment(pos.startLine); + + const info: IHDLTypeInfo = { + name, + typeExpression, + comment, + description: undefined, + position: pos, + }; + + result.push(new HDLType(info)); + + return result; + }; +} diff --git a/src/extractors/VariableExtractor.ts b/src/extractors/VariableExtractor.ts new file mode 100644 index 0000000..669a9de --- /dev/null +++ b/src/extractors/VariableExtractor.ts @@ -0,0 +1,66 @@ +/* eslint-disable max-len */ +/* eslint-disable no-console */ +import Parser from 'web-tree-sitter'; +import { Position } from '../elements/Common'; +import { HDLVariable } from '../elements/HDLVariable'; +import { + GetLeadingComment, + INodeExtractor, + NodeHandler, +} from './interfaces/IHDLElementExtractor'; +import { IHDLVariableInfo } from '../elements/interfaces/IHDLVariable'; + +/** + * VariableExtractor extracts variable declarations from a VHDL AST. + */ +export class VariableExtractor implements INodeExtractor { + /** + * @inheritdoc + */ + public readonly nodeType = 'variable_declaration'; + + /** + * @inheritdoc + */ + public readonly excludedParents = [ + 'function_body', + 'procedure_body', + 'process_statement', + ]; + + /** + * @inheritdoc + */ + public readonly nodeHandler: NodeHandler = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, + ): HDLVariable[] => { + const result: HDLVariable[] = []; + + const nameNode = node.descendantsOfType('identifier_list')[0]; + const typeNode = node.descendantsOfType('subtype_indication')[0]; + const defaultNode = node.descendantsOfType('default_expression')[0]; + + const names = nameNode.text.split(',').map((s) => s.trim()); + const type = typeNode?.text ?? ''; + const defaultValue = defaultNode?.text ?? undefined; + const pos = Position.fromNode(nameNode); + + for (const name of names) { + const comment = getLeadingComment(pos.startLine); + + const info: IHDLVariableInfo = { + name, + type, + defaultValue, + comment, + description: undefined, + position: pos, + }; + + result.push(new HDLVariable(info)); + } + + return result; + }; +} diff --git a/src/extractors/interfaces/IHDLElementExtractor.ts b/src/extractors/interfaces/IHDLElementExtractor.ts new file mode 100644 index 0000000..7e379d8 --- /dev/null +++ b/src/extractors/interfaces/IHDLElementExtractor.ts @@ -0,0 +1,73 @@ +import { IHDLElement, IHDLElementInfo } from 'elements/interfaces/IHDLElement'; +import Parser from 'web-tree-sitter'; +import { ICommentOptions } from './ICommentOptions'; + +/** + * Try to find a contiguous block of comment lines directly above the given line. + * Stops at the first non-comment or skipped line. + * @param line The line number to check for comments. + * @returns The comment block as a string, or undefined if no comments are found. + */ +export type GetLeadingComment = (line: number) => string | undefined; + +/** + * NodeHandler is a function type that takes a Parser.SyntaxNode and a GetLeadingComment function, + * and returns an array of elements of type T. + * @param node The syntax node to process. + * @param getLeadingComment A function to retrieve leading comments for the node. + * @returns An array of elements of type T. + */ +export type NodeHandler> = ( + node: Parser.SyntaxNode, + getLeadingComment: GetLeadingComment, +) => T[]; + +/** + * INodeExtractor is an interface that defines a method to extract elements from a syntax tree. + * It provides a node type and a handler function to process each node. + */ +export interface INodeExtractor> { + /** + * The type of node to extract from the syntax tree. + */ + readonly nodeType: string; + + /** + * Optional list of parent node types under which this element should be ignored. + */ + readonly excludedParents?: string[]; + + /** + * The handler function to process each node of the specified type. + * It takes a node and a function to get leading comments. + * @param node The syntax node to process. + * @param getLeadingComment A function to retrieve leading comments for the node. + * @returns An array of elements of type T. + */ + readonly nodeHandler: NodeHandler; +} + +/** + * IHDLElementExtractor_ is an interface that defines a constructor + * for extracting elements from a syntax tree. + * It takes a root node, optional comment options, a node type, and a handler function. + */ +export interface IHDLElementExtractor_< + _T extends IHDLElement, +> { + new ( + root: Parser.SyntaxNode, + commentOptions?: ICommentOptions, + nodeType?: string, + handler?: NodeHandler<_T>, + ): IHDLElementExtractor<_T>; +} + +export interface IHDLElementExtractor> { + /** + * Extract all elements of the specified type from the syntax tree. + * Uses the provided handler function to process each node. + * @returns An array of extracted elements. + */ + extract(): T[]; +} diff --git a/src/index.ts b/src/index.ts index bb91c23..0799818 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,52 @@ /* eslint-disable no-console */ + +import { HDLGeneric } from 'elements/HDLGeneric'; +import { HDLPort } from 'elements/HDLPort'; +import { HDLType } from 'elements/HDLType'; +import { AssignmentExtractor } from 'extractors/AssignmentExtractor'; +import { FunctionExtractor } from 'extractors/FunctionExtractor'; +import { GenericExtractor } from 'extractors/GenericExtractor'; +import { HDLElementExtractor } from 'extractors/HDLElementExtractor'; +import { InstantiationExtractor } from 'extractors/InstantiationExtractor'; +import { PortExtractor } from 'extractors/PortExtractor'; +import { ProcedureExtractor } from 'extractors/ProcedureExtractor'; +import { TypeExtractor } from 'extractors/TypeExtractor'; +import { VariableExtractor } from 'extractors/VariableExtractor'; import { writeFileSync } from 'fs'; import { join } from 'path'; +import Parser from 'web-tree-sitter'; import { HDLSignal } from './elements/HDLSignal'; +import { ConstantExtractor } from './extractors/ConstantExtractor'; +import { ProcessExtractor } from './extractors/ProcessExtractor'; import { SignalExtractor } from './extractors/SignalExtractor'; import { VHDLParser } from './parser/VHDLParser'; +/** + * Recursively converts a Tree-sitter SyntaxNode into a plain JSON object. + * @param node The Tree-sitter SyntaxNode. + * @returns A plain JSON object representing the node. + */ +function syntaxNodeToJson(node: Parser.SyntaxNode): unknown { + return { + type: node.type, + text: node.text, + startPosition: node.startPosition, + endPosition: node.endPosition, + children: node.namedChildren.map(syntaxNodeToJson), + }; +} + +/** + * Dumps the full AST of a Tree-sitter parsed file to a JSON file. + * @param rootNode The root node of the syntax tree. + * @param outputPath The path to the output JSON file. + */ +export function dumpAST(rootNode: Parser.SyntaxNode, outputPath: string): void { + const ast = syntaxNodeToJson(rootNode); + writeFileSync(outputPath, JSON.stringify(ast, null, 2)); + console.log(`🧠 AST written to ${outputPath}`); +} + /** * Main function to extract signals from a VHDL file and write them to a JSON file. */ @@ -15,16 +57,37 @@ async function main(): Promise { const filePath = join(__dirname, '../test/AsyncFIFO.vhd'); const tree = await parser.parseFile(filePath); - const extractor = new SignalExtractor(tree.rootNode, { + dumpAST(tree.rootNode, join(__dirname, '../test/ast.json')); + + const commentOptions = { markerPrefix: '@', stripPrefix: true, - }); - const signals: HDLSignal[] = extractor.extract(); + }; - const output = join(__dirname, '../test/signals.json'); + const processExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new ProcessExtractor(), + ); + + const processes = processExtractor.extract(); writeFileSync( - output, + join(__dirname, '../test/processes.json'), + JSON.stringify(processes, null, 2), + ); + console.log('✅ Processes written to test/processes.json'); + + const extractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new SignalExtractor(), + ); + + const signals: HDLSignal[] = extractor.extract(); + + writeFileSync( + join(__dirname, '../test/signals.json'), JSON.stringify( signals.map((s) => s.info), null, @@ -32,7 +95,150 @@ async function main(): Promise { ), ); - console.log('✅ Signals written to:', output); + console.log('✅ Signals written to test/signals.json'); + + const constantExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new ConstantExtractor(), + ); + + const constants = constantExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/constants.json'), + JSON.stringify(constants, null, 2), + ); + console.log('✅ Constants written to test/constants.json'); + + const instantiationExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new InstantiationExtractor(), + ); + + const instantiations = instantiationExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/instantiations.json'), + JSON.stringify(instantiations, null, 2), + ); + console.log('✅ Instantiations written to test/instantiations.json'); + + const functionExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new FunctionExtractor(), + ); + + const functions = functionExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/functions.json'), + JSON.stringify( + functions.map((f) => f.info), + null, + 2, + ), + ); + console.log('✅ Functions written to test/functions.json'); + + const assignmentExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new AssignmentExtractor(), + ); + + const assignments = assignmentExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/assignments.json'), + JSON.stringify(assignments, null, 2), + ); + console.log('✅ Assignments written to test/assignments.json'); + + const variableExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new VariableExtractor(), + ); + + const variables = variableExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/variables.json'), + JSON.stringify(variables, null, 2), + ); + console.log('✅ Variables written to test/variables.json'); + + const procedureExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new ProcedureExtractor(), + ); + + const procedures = procedureExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/procedures.json'), + JSON.stringify(procedures, null, 2), + ); + console.log('✅ Procedures written to test/procedures.json'); + + const typeExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new TypeExtractor(), + ); + + const types = typeExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/types.json'), + JSON.stringify( + types.map((t) => t.info), + null, + 2, + ), + ); + console.log(`✅ Types written to test/types.json`); + + const genericExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new GenericExtractor(), + ); + + const generics = genericExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/generics.json'), + JSON.stringify( + generics.map((g) => g.info), + null, + 2, + ), + ); + console.log(`✅ Generics written to test/generics.json`); + + const portExtractor = new HDLElementExtractor( + tree.rootNode, + commentOptions, + new PortExtractor(), + ); + + const port = portExtractor.extract(); + + writeFileSync( + join(__dirname, '../test/ports.json'), + JSON.stringify( + port.map((p) => p.info), + null, + 2, + ), + ); + + console.log(`✅ Ports written to test/ports.json`); } main().catch((e) => console.error('❌ Signal extraction failed:', e)); diff --git a/tsconfig.json b/tsconfig.json index d0e59c7..8a7ba49 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ "experimentalDecorators": true, "emitDecoratorMetadata": true, "strict": true, + "strictFunctionTypes": false, "strictNullChecks": true, "strictPropertyInitialization": true, "forceConsistentCasingInFileNames": true,