UNPKG

pss-langserver

Version:

A Language server for the Portable Stimulus Standard

1,103 lines 62.3 kB
"use strict"; /* * Copyright (C) 2025 Darshan(@thisisthedarshan) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.advancedVisitor = void 0; const antlr4_1 = require("antlr4"); const pssVisitor_1 = __importDefault(require("../grammar/pssVisitor")); const helpers_1 = require("./helpers"); const dataTypes_1 = require("../definitions/dataTypes"); const doxygenLexer_1 = __importDefault(require("../grammar/doxygenLexer")); const doxygenParser_1 = __importDefault(require("../grammar/doxygenParser")); const visitors_1 = require("./visitors"); const typeGuards_1 = require("./typeGuards"); class advancedVisitor extends pssVisitor_1.default { constructor(tokenStream, fileURI) { super(); this.astObjects = []; this.currentASTHierarchy = []; this.sharedData = false; this.fileDocument = ""; this.getFileDoc = () => { return this.fileDocument; }; this.getAstObjects = () => { return this.astObjects; }; this.tokenStream = tokenStream; /* Define function to use internally */ const addNodeToParent = (node) => { if (this.currentASTHierarchy.length > 0) { /* Add to the current parent's children */ const parent = this.currentASTHierarchy[this.currentASTHierarchy.length - 1]; parent.children.push(node); } else { /* Top-level node */ this.astObjects.push(node); } }; /* Visit the start of the file */ this.visitPss_entry = (ctx) => { if (ctx.file_doc_comment()?.documentation_comment()) { const fileURL = new URL(fileURI); const filePath = fileURL.pathname.split('/').pop; const fileName = (filePath === undefined) ? '' : filePath.toString(); let inputStream = new antlr4_1.CharStream(ctx.file_doc_comment().documentation_comment().getText()); let doxygenLex = new doxygenLexer_1.default(inputStream); let tokenStream = new antlr4_1.CommonTokenStream(doxygenLex); let parser = new doxygenParser_1.default(tokenStream); parser.removeErrorListeners(); let doxygenVisitor = new visitors_1.doxygen_visitor(fileName, true); let docsTree = parser.documentation_comment(); docsTree.accept(doxygenVisitor); this.fileDocument = doxygenVisitor.getComment(); } this.visitChildren(ctx); return this.astObjects[0]; }; /* Template visitor that visits template items and returns it through shared object */ this.visitTemplate_param_decl_list = (ctx) => { const templateParams = []; ctx.template_param_decl_list().map(templateItem => { const type_param = templateItem.type_param_decl(); const value_param = templateItem.value_param_decl(); if (type_param) { const genericType = type_param.generic_type_param_decl(); const categoryType = type_param.category_type_param_decl(); if (genericType) { templateParams.push({ paramName: genericType.identifier()?.getText() ?? "", paramType: "undefined", paramDefault: genericType.type_identifier()?.getText() ?? undefined }); } if (categoryType) { templateParams.push({ paramName: categoryType.identifier()?.getText() ?? "", paramType: categoryType.type_category()?.getText() ?? "unknown", paramDefault: categoryType.type_identifier()?.getText() ?? undefined }); } } if (value_param) { templateParams.push({ paramName: value_param.identifier()?.getText() ?? "", paramType: value_param.data_type()?.getText() ?? "", paramDefault: value_param.constant_expression()?.getText() ?? undefined }); } }); /* Prepare data to be put onto shared object */ this.sharedData = templateParams; }; /* Visit data declarations */ this.visitData_declaration = (ctx) => { /* Check if they are random and static-constants */ let isStaticConst = false; let isRandom = false; let accessModifier = ""; if (typeof this.sharedData === 'boolean') { isStaticConst = this.sharedData; } else if (Array.isArray(this.sharedData)) { isStaticConst = typeof this.sharedData[0] === 'boolean' ? this.sharedData[0] : false; isRandom = typeof this.sharedData[1] === 'boolean' ? this.sharedData[1] : false; accessModifier = typeof this.sharedData[2] === 'string' ? this.sharedData[2] : 'public'; } /* Get other information like instance type, name, value, array count and definition location */ const dataType = ctx.data_type().getText(); /* Empty node shell for return value */ let node; /* There can be multiple instances, we iterate through all of them */ ctx.data_instantiation_list().map(instance => { /* Get values from declaration */ const name = instance.identifier().getText(); const arrayDim = instance.array_dim()?.constant_expression()?.getText() ?? "0"; const definedOn = { file: fileURI, lineNumber: instance.start.line, columnNumber: instance.start.column }; const value = instance.constant_expression()?.getText() ?? ""; /* Generate node from the data obtained */ const instanceNode = { type: dataTypes_1.objType.INSTANCE, instanceType: dataType, instanceDefaultValue: value, instanceArrayCount: arrayDim, isRandom: isRandom, isStaticConst: isStaticConst, name: name, definedOn: definedOn, comments: "", children: [], accessModifier: accessModifier }; addNodeToParent(instanceNode); /* Whenever we visit a data declaration, its always an end node */ }); }; /* Action related crawler */ /** * @brief This is the logic to visit an action declaration. */ this.visitAction_declaration = (ctx) => { /* Prepare data to be put onto the AST */ /* Check if it is an abstract action - set when the visitor visits the abstract action declaration */ let isAbstract = false; if (typeof (this.sharedData) === 'boolean') { isAbstract = this.sharedData; this.sharedData = false; } /* Get name of the action */ const actionName = ctx.action_identifier()?.getText() ?? "undefined action"; /* Get name of the super-spec, if any */ const superSpec = ctx.action_super_spec()?.type_identifier()?.getText() ?? ""; /* Get items of the template, if any... */ let templateParams = []; if (ctx.template_param_decl_list()) { this.sharedData = `for ${actionName}`; this.visit(ctx.template_param_decl_list()); if (Array.isArray(this.sharedData) && (0, typeGuards_1.isValidParams)(this.sharedData[0])) { templateParams = this.sharedData; } } /* Get definition location */ let definedOn = { file: fileURI, lineNumber: ctx.action_identifier().start.line, columnNumber: ctx.action_identifier().start.column }; /* Load the data onto ast */ let node = { name: actionName, type: dataTypes_1.objType.ACTION, isAbstract: isAbstract, templateParams: templateParams, superSpec: superSpec, definedOn: definedOn, comments: "", children: [] }; const baseNode = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], actionName); if (baseNode) { node = baseNode; node.isAbstract = isAbstract; node.templateParams = templateParams; node.superSpec = superSpec; node.definedOn = definedOn; } addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(ctx); this.currentASTHierarchy.pop(); return node; }; /** * @brief Visits an abstract action */ this.visitAbstract_action_declaration = (ctx) => { /* Indicate its an abstract action */ this.sharedData = true; /* Traverse the action declaration */ this.visit(ctx.action_declaration()); }; /* Visit attr_field traversal */ this.visitAttr_field = (ctx) => { const accessModifier = ctx.access_modifier()?.getText() || "public"; const isRandom = Boolean(ctx.TOKEN_RAND()); /* According to PSS standard, we are supposed to define both `static const` keywords for it to count. So, only checking const keyword works */ const isStaticConst = Boolean(ctx.TOKEN_CONST()); this.sharedData = [isStaticConst, isRandom, accessModifier]; this.visit(ctx.data_declaration()); }; /* Visit object flow reference field declaration */ this.visitFlow_ref_field_declaration = (ctx) => { /* This is creating a flow object instance of input/output type */ const direction = (ctx.TOKEN_INPUT()) ? 'input' : 'output'; const type = (0, helpers_1.getFlowObjType)(ctx.flow_object_type().getText()); let node; ctx.object_ref_field_list().map(objRef => { node = { type: type, flowDirection: direction, name: objRef.identifier().getText(), definedOn: { file: fileURI, lineNumber: objRef.identifier().start.line, columnNumber: objRef.identifier().start.column }, comments: "", children: [], instanceArraySize: (objRef.array_dim()) ? objRef.array_dim().constant_expression()?.getText() : 0 }; addNodeToParent(node); }); }; /* Visit object resource reference field declaration */ this.visitResource_ref_field_declaration = (ctx) => { /* This is creating a resource object instance of input/output type */ const direction = (ctx.TOKEN_LOCK()) ? 'lock' : 'share'; let node; ctx.object_ref_field_list().map(objRef => { node = { type: dataTypes_1.objType.RESOURCE_OBJECT, resourceLock: direction, name: objRef.identifier().getText(), definedOn: { file: fileURI, lineNumber: objRef.identifier().start.line, columnNumber: objRef.identifier().start.column }, comments: "", children: [], instanceArraySize: (objRef.array_dim()) ? objRef.array_dim().constant_expression()?.getText() : 0 }; addNodeToParent(node); }); }; /* Action handle declaration visitor */ this.visitAction_handle_declaration = (ctx) => { let node; const type = ctx.action_type_identifier().getText(); ctx.action_instantiation().action_handle_identifier_list().map((instance, idx) => { node = { type: dataTypes_1.objType.ACTION_HANDLE, accessModifier: "local", instanceType: type, instanceDefaultValue: "", instanceArrayCount: ctx.action_instantiation().array_dim(idx)?.getText() ?? 0, isRandom: false, isStaticConst: false, name: instance.getText(), definedOn: { file: fileURI, lineNumber: instance.start.line, columnNumber: instance.start.column }, comments: "", children: [] }; addNodeToParent(node); }); }; /* Registers related crawler */ /** Register component definition visitor */ this.visitRegister_comp_definition = (ctx) => { const registerStruct = ctx.reg_struct_identifier().getText(); const accessType = dataTypes_1.accessMap[ctx.access_type()?.getText().toUpperCase()] ?? "READWRITE"; const packedStructSize = (0, helpers_1.getPackedStructSize)([...this.astObjects, ...this.currentASTHierarchy], registerStruct); const regLength = Number(ctx.reg_length()?.getText() ?? packedStructSize); const node = { type: dataTypes_1.objType.REGISTER_COMP, accessType: accessType, registerLength: regLength, basedOnStruct: registerStruct, name: ctx.register_comp_identifier().getText(), definedOn: { file: fileURI, lineNumber: ctx.start.line, columnNumber: ctx.start.column }, comments: "", children: [] }; addNodeToParent(node); return node; }; /** Visit a register group definition */ this.visitRegister_group_definition = (ctx) => { const node = { type: dataTypes_1.objType.REGISTER_GROUP, baseAddress: "unknown", name: ctx.register_group_identifier().getText(), definedOn: { file: fileURI, lineNumber: ctx.register_group_identifier().start.line, columnNumber: ctx.register_group_identifier().start.column }, comments: "", children: [] }; addNodeToParent(node); this.currentASTHierarchy.push(node); ctx.register_body_definition_list().forEach(definition => { this.visit(definition); }); this.currentASTHierarchy.pop(); return node; }; /** Visit register body definition for a register group */ this.visitRegister_body_definition = (ctx) => { this.visitChildren(ctx); }; /** Visit register component instance */ this.visitRegister_comp_instance = (ctx) => { const node = { type: dataTypes_1.objType.REGISTER, accessModifier: "private", instanceType: ctx.register_group_identifier().getText(), instanceDefaultValue: "", instanceArrayCount: Number(ctx.integer_number()?.getText() ?? 0), isRandom: false, isStaticConst: false, name: ctx.register_identifier().getText(), definedOn: { file: fileURI, lineNumber: ctx.register_group_identifier().start.line, columnNumber: ctx.register_group_identifier().start.column }, comments: "", children: [] }; addNodeToParent(node); return node; }; /** Visit register definition - Direct declaration of a register component instance */ this.visitRegister_definition = (ctx) => { const regStruct = ctx.reg_struct_identifier().getText(); const accessType = dataTypes_1.accessMap[ctx.access_type()?.getText().toUpperCase()] ?? "READWRITE"; const packedStructSize = (0, helpers_1.getPackedStructSize)([...this.astObjects, ...this.currentASTHierarchy], regStruct); const regLength = Number(ctx.reg_length()?.integer_number()?.getText() ?? packedStructSize); const instanceName = ctx.register_identifier().getText(); const arraySize = Number(ctx.integer_number()?.getText() ?? 0); let node = { type: dataTypes_1.objType.REGISTER_DEF, accessType: accessType, registerLength: regLength, basedOnStruct: regStruct, instanceArrayCount: arraySize, name: instanceName, definedOn: { file: fileURI, lineNumber: ctx.start.line, columnNumber: ctx.start.column }, comments: "", children: [] }; addNodeToParent(node); return node; }; /** Future plans - use reg_set_handle to obtain base addresses */ /** Address space related crawlers */ /** Contiguous address space definition */ this.visitContiguous_addr_space_def = (ctx) => { const addrTrait = ctx.addr_space_traits()?.getText() || ""; let node; ctx.addr_space_identifier_list().forEach(identifier => { node = { type: dataTypes_1.objType.ADDRESS_SPACE, addressType: "contiguous", traitsStruct: addrTrait, size: "", address: "", isRandom: false, additionalTraits: [], name: identifier.getText(), definedOn: { file: fileURI, lineNumber: identifier.start.line, columnNumber: identifier.start.column }, comments: "", regions: [], children: [] }; addNodeToParent(node); }); }; /** Address Space settings */ this.visitAddr_region_setting = (ctx) => { const identifier = ctx.addr_region_identifier().getText(); const regionNodeRoot = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], identifier); if (typeof (regionNodeRoot) === 'undefined') { return; } const regionNode = regionNodeRoot; if (ctx._size_i || ctx._size_e) { regionNode.size = ctx.integer_number()?.getText() || ctx.expression()?.getText() || ""; } else if (ctx._addr_i || ctx._addr_e) { regionNode.address = ctx.integer_number()?.getText() || ctx.expression()?.getText() || ""; } else if (ctx._trait) { regionNode.additionalTraits?.push({ [ctx.trait_identifier().getText()]: ctx.trait_property().getText() }); } }; /** Visit address claim */ this.visitAddr_claim = (ctx) => { const isRand = Boolean(ctx.TOKEN_RAND()); const traitIdentifier = ctx.trait_identifier()?.getText() || ""; ctx.claim_identifier_list().forEach(claim => { const node = { type: dataTypes_1.objType.ADDRESS_CLAIM, isRandom: isRand, name: claim.getText(), definedOn: { file: fileURI, lineNumber: claim.start.line, columnNumber: claim.start.column }, comments: "", children: [], addressType: "contiguous", traitsStruct: traitIdentifier, size: "", address: "", regions: [], additionalTraits: [] }; addNodeToParent(node); }); }; /** Visit transparent address space definition */ this.visitTransparent_addr_space_def = (ctx) => { const traitsIdentifier = ctx.addr_space_traits()?.getText() || ""; let node; ctx.addr_space_identifier_list().forEach(id => { node = { type: dataTypes_1.objType.ADDRESS_SPACE, addressType: "transparent", traitsStruct: traitsIdentifier, size: "", address: "", isRandom: false, additionalTraits: [], name: id.getText(), definedOn: { file: fileURI, lineNumber: id.start.line, columnNumber: id.start.column }, comments: "", regions: [], children: [] }; addNodeToParent(node); }); }; /** Visit transparent address region definition */ this.visitTransparent_addr_region_def = (ctx) => { const traitsIdentifier = ctx.addr_space_traits()?.getText() || ""; ctx.addr_region_identifier_list().forEach(identifier => { let node = { type: dataTypes_1.objType.ADDRESS_REGION, isRandom: false, addressType: "transparent", traitsStruct: traitsIdentifier, size: "", address: "", regions: [], additionalTraits: [], name: identifier.getText(), definedOn: { file: fileURI, lineNumber: identifier.start.line, columnNumber: identifier.start.column }, comments: "", children: [] }; addNodeToParent(node); }); }; /** Visitor for transparent address claim */ this.visitTransparent_addr_claim = (ctx) => { const isRand = Boolean(ctx.TOKEN_RAND()); const traitIdentifier = ctx.trait_identifier()?.getText() || ""; ctx.claim_identifier_list().forEach(identifier => { const node = { type: dataTypes_1.objType.ADDRESS_CLAIM, isRandom: isRand, addressType: "transparent", traitsStruct: traitIdentifier, size: "", address: "", regions: [], additionalTraits: [], name: identifier.getText(), definedOn: { file: fileURI, lineNumber: identifier.start.line, columnNumber: identifier.start.column }, comments: "", children: [] }; addNodeToParent(node); }); }; /** Add regions -> Non-allocatable */ this.visitAdd_addr_region_nonallocatable = (ctx) => { const identifier = ctx.addr_space_identifier().getText(); const regionIdentifier = ctx.addr_region_identifier().getText(); const nodeRoot = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], identifier); if (nodeRoot) { const node = nodeRoot; node.regions.push({ [regionIdentifier]: "non-allocatable" }); } if (ctx.addr_handle_identifier()) { const nodeBase = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], ctx.addr_handle_identifier().getText()); if (nodeBase) { const node = nodeBase; /** Do something here, like setting the value? */ node.instanceDefaultValue = `value of ${identifier} region (non-allocatable)`; } } }; /** Add regions -> Allocatable */ this.visitAdd_addr_region = (ctx) => { const identifier = ctx.addr_space_identifier().getText(); const regionIdentifier = ctx.addr_region_identifier().getText(); const nodeRoot = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], identifier); if (nodeRoot) { const node = nodeRoot; node.regions.push({ [regionIdentifier]: "allocatable" }); } if (ctx.addr_handle_identifier()) { const nodeBase = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], ctx.addr_handle_identifier().getText()); if (nodeBase) { const node = nodeBase; /** Do something here, like setting the value? */ node.instanceDefaultValue = `value of ${identifier} region`; } } }; /* Component Related crawler */ /* Visit the component declaration */ this.visitComponent_declaration = (ctx) => { const isPure = Boolean(ctx.TOKEN_PURE()); const name = ctx.component_identifier().getText(); var params = []; if (ctx.template_param_decl_list()) { this.sharedData = name; this.visit(ctx.template_param_decl_list()); params = this.sharedData; } const superSpec = ctx.component_super_spec()?.type_identifier()?.getText() || ""; const baseNode = (0, helpers_1.getNodeFromNameArray)([...this.currentASTHierarchy, ...this.astObjects], name); let node; if (baseNode) { node = baseNode; node.isPure = isPure; node.templateParams = params; node.superSpec = superSpec; node.definedOn = { file: fileURI, lineNumber: ctx.component_identifier().start.line, columnNumber: ctx.component_identifier().start.column }; node.comments = ""; } else { node = { type: dataTypes_1.objType.COMPONENT, isPure: isPure, templateParams: params, superSpec: superSpec, name: name, definedOn: { file: fileURI, lineNumber: ctx.component_identifier().start.line, columnNumber: ctx.component_identifier().start.column }, comments: "", children: [] }; } addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(ctx); this.currentASTHierarchy.pop(); return node; }; /** Visit data declarations done on a component level */ this.visitComponent_data_declaration = (ctx) => { const accessModifier = ctx.access_modifier()?.getText() ?? "public"; const isStatic = Boolean(ctx.TOKEN_STATIC()); this.sharedData = []; this.sharedData[0] = isStatic; this.sharedData[1] = false; this.sharedData[2] = accessModifier; this.visit(ctx.data_declaration()); }; /** Visit component pool declaration */ this.visitComponent_pool_declaration = (ctx) => { const node = { type: dataTypes_1.objType.POOLS, accessModifier: "public", instanceType: ctx.type_identifier().getText(), instanceDefaultValue: "", instanceArrayCount: ctx.expression()?.getText() ?? "", isRandom: false, isStaticConst: false, name: ctx.identifier().getText(), definedOn: { file: fileURI, lineNumber: ctx.identifier().start.line, columnNumber: ctx.identifier().start.column }, comments: "", children: [] }; addNodeToParent(node); }; /** Visit exec block statements - 3 types - regular, target and file */ this.visitExec_block_stmt = (ctx) => { if (ctx.exec_block()) { this.visit(ctx.exec_block()); } else if (ctx.target_code_exec_block()) { /** For target language */ const node = { type: (0, helpers_1.getExecType)(ctx.target_code_exec_block().exec_kind()?.getText() ?? ""), content: ctx.target_code_exec_block().string_literal().getText(), name: `${ctx.target_code_exec_block().exec_kind().getText()} - ${ctx.target_code_exec_block().language_identifier().getText()}`, definedOn: { file: fileURI, lineNumber: ctx.target_code_exec_block().start.line, columnNumber: ctx.target_code_exec_block().start.column }, comments: "", children: [], execType: "target" }; addNodeToParent(node); } else if (ctx.target_file_exec_block()) { /** For target language */ const node = { type: dataTypes_1.objType.EXEC_POSTSOLVE, /** Since file is generated after solving */ content: ctx.target_file_exec_block().string_literal().getText(), name: ctx.target_file_exec_block().filename_string().getText(), definedOn: { file: fileURI, lineNumber: ctx.target_file_exec_block().start.line, columnNumber: ctx.target_file_exec_block().start.column }, comments: "", children: [], execType: "file" }; addNodeToParent(node); } }; /** Exec block visitor */ this.visitExec_block = (ctx) => { let contents = ""; if (ctx.exec_stmt_list()) { ctx.exec_stmt_list().forEach(stmt => { contents += (stmt.getText() + '\n'); }); } const node = { type: (0, helpers_1.getExecType)(ctx.exec_kind()?.getText() || ""), execType: "regular", content: contents, name: "", definedOn: { file: fileURI, lineNumber: ctx.exec_kind().start.line, columnNumber: ctx.exec_kind().start.column }, comments: "", children: [] }; addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(ctx); this.currentASTHierarchy.pop(); }; /** Visit package statements */ this.visitPackage_declaration = (ctx) => { const identifier = ctx.package_identifier().getText(); let path = ""; for (let idx = 1; idx < ctx.package_id_path_list().length; idx++) { path += `:: ${ctx.package_id_path(idx).getText()}`; } const node = { type: dataTypes_1.objType.PACKAGE, path: path, name: identifier, definedOn: { file: fileURI, lineNumber: ctx.package_identifier().start.line, columnNumber: ctx.package_identifier().start.column }, comments: "", children: [] }; addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(ctx); this.currentASTHierarchy.pop(); return node; }; /** Visit struct declaration */ this.visitStruct_declaration = (ctd) => { const identifier = ctd.struct_identifier().getText(); let templateParams = []; if (ctd.template_param_decl_list()) { this.visit(ctd.template_param_decl_list()); templateParams = this.sharedData; } let node = { type: (0, helpers_1.getStructKind)(ctd.struct_kind().getText()), superSpec: ctd.struct_super_spec()?.getText() ?? "", name: identifier, definedOn: { file: fileURI, lineNumber: ctd.start.line, columnNumber: ctd.start.column }, comments: "", templateParams: templateParams, children: [] }; const rootNode = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], identifier); if (rootNode) { node = rootNode; node.type = (0, helpers_1.getStructKind)(ctd.struct_kind().getText()); node.superSpec = ctd.struct_super_spec()?.getText() ?? ""; node.definedOn = { file: fileURI, lineNumber: ctd.start.line, columnNumber: ctd.start.column }; node.comments = ""; node.templateParams = templateParams; } addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(ctd); this.currentASTHierarchy.pop(); return node; }; /** Visit typedef declaration */ this.visitTypedef_declaration = (d) => { const node = { type: dataTypes_1.objType.TYPEDEF, dataType: d.data_type().getText(), name: d.identifier().getText(), definedOn: { file: fileURI, lineNumber: d.identifier().start.line, columnNumber: d.identifier().start.column }, comments: "", children: [] }; addNodeToParent(node); }; /** Visit enum declaration */ this.visitEnum_declaration = (d) => { const identifier = d.enum_identifier().getText(); const dataType = d.data_type()?.getText() ?? "int"; /** We assume the base type to be of type int */ let enumItems = []; d.enum_item_list()?.forEach((enumItem, idx) => { if (enumItem.identifier()) { this.sharedData = idx; /** The below logic is helpful */ if (enumItems.length > 0) { this.sharedData = enumItems[enumItems.length - 1].value + 1; } this.visit(enumItem); enumItems.push(this.sharedData); } }); const baseNode = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], identifier); let node = { type: dataTypes_1.objType.ENUM, dataType: dataType, enumItems: enumItems, name: identifier, definedOn: { file: fileURI, lineNumber: d.enum_identifier().start.line, columnNumber: d.enum_identifier().start.column }, comments: "", children: [] }; if (baseNode) { node = baseNode; node.definedOn = { file: fileURI, lineNumber: d.enum_identifier().start.line, columnNumber: d.enum_identifier().start.column }; node.dataType = dataType; node.enumItems = [...node.enumItems, ...enumItems]; } addNodeToParent(node); }; /** Visitor for enum items */ this.visitEnum_item = (d) => { let item = { name: d.identifier().getText(), value: Number(d.integer_number()?.getText() ?? this.sharedData), definedOn: { file: fileURI, lineNumber: d.identifier().start.line, columnNumber: d.identifier().start.column } }; this.sharedData = item; }; /** Visitor for function parameter */ this.visitFunction_parameter = (d) => { let param = { paramType: `${d.function_parameter_dir()?.getText() ?? d.TOKEN_CONST()?.getText() ?? ''}`, paramName: d.identifier()?.getText() ?? d.user_type().getText().trim().split(/\s+/)[1], paramDefault: d.constant_expression()?.getText() ?? "" }; if (d.data_type()) { /** First scenario - data type is given */ param.paramType = `${param.paramType} ${d.data_type().getText()}`; } else if (d.user_type()) { /* 2nd scenario - user defined data type */ param.paramType = `${param.paramType} ${d.user_type().getText().trim().split(/\s+/)[0]}`; } else { /** 3rd scenario */ param.paramType = `${d.TOKEN_CONST()?.getText() ?? ""} ${d.TOKEN_TYPE()?.getText() ?? d.type_category()?.getText() ?? d.TOKEN_STRUCT()?.getText() ?? ""}`; } param.paramType = param.paramType.trim(); this.sharedData = param; }; /** Visitor for function parameter list prototype */ this.visitFunction_parameter_list_prototype = (d) => { const params = []; d.function_parameter_list().forEach(param => { this.visit(param); params.push(this.sharedData); }); if (d.varargs_parameter()) { const vararg = d.varargs_parameter(); const dataType = vararg.data_type()?.getText() ?? vararg.TOKEN_TYPE()?.getText() ?? vararg.type_category()?.getText() ?? vararg.TOKEN_STRUCT()?.getText() ?? ""; params.push({ paramType: "...args", paramName: d.varargs_parameter().identifier().getText(), paramDefault: dataType /** This is just for reference */ }); } this.sharedData = []; this.sharedData = params; }; /** Visitor for function prototype */ this.visitFunction_prototype = (d) => { const returnType = d.function_return_type().getText(); const identifier = d.function_identifier().getText(); const definedOn = { file: fileURI, lineNumber: d.function_identifier().start.line, columnNumber: d.function_identifier().start.column }; this.visit(d.function_parameter_list_prototype()); const params = this.sharedData; this.sharedData = []; this.sharedData[0] = returnType; this.sharedData[1] = identifier; this.sharedData[2] = params; this.sharedData[3] = definedOn; }; /** Visitor for function declaration */ this.visitFunction_decl = (d) => { const platformQualifier = (d.platform_qualifier()?.getText() ?? 'target') === 'solve' ? 'solve' : 'target'; const isPure = Boolean(d.TOKEN_PURE()); const isStatic = Boolean(d.TOKEN_STATIC()); this.visit(d.function_prototype()); const returnType = this.sharedData[0]; const identifier = this.sharedData[1]; const params = this.sharedData[2]; const definedOn = this.sharedData[3]; const node = { type: dataTypes_1.objType.FUNCTION, platformQualifier: platformQualifier, isPure: isPure, isStatic: isStatic, returnType: returnType, parameters: params, name: identifier, definedOn: definedOn, comments: "", children: [] }; addNodeToParent(node); }; /** Visitor for procedural function */ this.visitProcedural_function = (d) => { const platformQualifier = (d.platform_qualifier()?.getText() ?? 'target') === 'solve' ? 'solve' : 'target'; const isPure = Boolean(d.TOKEN_PURE()); const isStatic = Boolean(d.TOKEN_STATIC()); this.visit(d.function_prototype()); const returnType = this.sharedData[0]; const identifier = this.sharedData[1]; const params = this.sharedData[2]; const definedOn = this.sharedData[3]; const node = { type: dataTypes_1.objType.FUNCTION, platformQualifier: platformQualifier, isPure: isPure, isStatic: isStatic, returnType: returnType, parameters: params, name: identifier, definedOn: definedOn, comments: "", children: [] }; addNodeToParent(node); this.currentASTHierarchy.push(node); this.visitChildren(d); this.currentASTHierarchy.pop(); }; /** Visitor for procedural statements */ /** Visit procedural data declaration */ this.visitProcedural_data_declaration = (d) => { let dataType; if (d.data_type()) { dataType = d.data_type().getText(); } else if (d.user_type()) { const dataInfo = d.user_type().getText().trim().split(/\s+/); dataType = dataInfo[0]; const dataID = dataInfo[1]; const trimmed = d.user_type().getText().trimStart(); const firstSpace = trimmed.indexOf(' '); const offsetOfDataID = (firstSpace === -1) ? 0 : firstSpace + 1 + (d.user_type().getText().length - trimmed.length); const node = { type: dataTypes_1.objType.INSTANCE, accessModifier: "", instanceType: dataType, instanceDefaultValue: d.constant_expression()?.getText() ?? "", instanceArrayCount: d.array_dim()?.constant_expression()?.getText() ?? "", isRandom: false, isStaticConst: false, name: dataID, definedOn: { file: fileURI, lineNumber: d.user_type().start.line, columnNumber: d.user_type().start.column + offsetOfDataID /** Adjusting to start from data type + space */ }, comments: "", children: [] }; addNodeToParent(node); } else { dataType = "unknown"; } d.procedural_data_instantiation_list()?.forEach(inst => { const node = { type: dataTypes_1.objType.INSTANCE, accessModifier: "", instanceType: dataType, instanceDefaultValue: inst.constant_expression()?.getText() ?? "", instanceArrayCount: inst.array_dim()?.constant_expression()?.getText() ?? "", isRandom: false, isStaticConst: false, name: inst.identifier().getText(), definedOn: { file: fileURI, lineNumber: inst.start.line, columnNumber: inst.start.column }, comments: "", children: [] }; addNodeToParent(node); }); }; /** Visit procedural assignment */ this.visitProcedural_assignment_stmt = (d) => { let node = { type: dataTypes_1.objType.ASSIGNMENT, operation: "=", value: "", name: "", dataType: "", definedOn: { file: fileURI, lineNumber: d.start.line, columnNumber: d.start.column }, comments: "", children: [] }; const refPath = d.ref_path(); const identifier = d.constant_expression(); const expression = d.expression(); const funcCall = d.function_call(); if (refPath) { node.name = refPath.getText(); const splitName = node.name.split('.'); if (splitName.length > 1) { node.name = splitName[0]; node.dataType = "struct->" + splitName.slice(1).join('->'); } else { node.dataType = "ref"; } node.value = d.expression().getText(); } else if (identifier && expression) { node.name = d.constant_expression().getText(); node.value = d.expression().getText(); const result = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], node.name); const nodeParent = (result) ? result : undefined; node.dataType = nodeParent?.instanceType ?? "unknown"; } else if (identifier && funcCall) { node.name = d.constant_expression().getText(); node.value = d.function_call().function_identifier().getText(); const result = (0, helpers_1.getNodeFromNameArray)([...this.astObjects, ...this.currentASTHierarchy], node.name); const nodeParent = (result) ? result : undefined; node.dataType = nodeParent?.instanceType ?? "unknown"; } const nameSplit = node.name.split(/\s+/); if (identifier && nameSplit.length > 1) { /* This is the case where we might d.data_type()?.getText() ?? have accidentally detected a user-defined data type instance creation */ const instanceNode = { type: dataTypes_1.objType.INSTANCE, accessModifier: "", instanceType: nameSplit[0], instanceDefaultValue: node.value, instanceArrayCount: 0, isRandom: false, isStaticConst: false, name: nameSplit[1], definedOn: node.definedOn, comments: "", children: [] }; addNodeToParent(instanceNode); } else { addNodeToParent(node); } }; /** Import statement visitors */ this.visitImport_function = (d) => { let identifier = ""; let content = ""; let de