UNPKG

teroshdl

Version:

Backend for terosHDL IDE

901 lines (841 loc) 27.3 kB
/* eslint-disable no-empty */ // Copyright 2020 Teros Technology // // Ismael Perez Rojo // Carlos Alberto Ruiz Naranjo // Alfredo Saez // // This file is part of Colibri. // // Colibri 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. // // Colibri 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 Colibri. If not, see <https://www.gnu.org/licenses/>. const path = require('path'); const ts_base_parser = require('./ts_base_parser'); const stm_parser = require('./vhdl_sm'); class Parser extends ts_base_parser.Ts_base_parser { constructor(comment_symbol) { super(); this.comment_symbol = comment_symbol; this.loaded = false; } async init() { try{ if (this.loaded !== true){ this.code = ''; const Parser = require('web-tree-sitter'); await Parser.init(); this.parser = new Parser(); let Lang = await Parser.Language.load(path.join(__dirname, path.sep + "parsers" + path.sep + "tree-sitter-vhdl.wasm")); this.parser.setLanguage(Lang); this.loaded = true; this.stm_parser = new stm_parser.Paser_stm_vhdl(this.comment_symbol, this.parser); } } catch(e){} } async get_svg_sm(code, symbol) { return this.stm_parser.get_svg_sm(code, symbol); } parse(code) { if (this.code !== code) { this.tree = this.parser.parse(code); this.code = code; } return this.tree; } get_all(code, comment_symbol) { this.set_symbol(comment_symbol); if (this.loaded === false){ return undefined; } let struct; let entity_file = this.get_entity_file(code); if (entity_file === undefined || entity_file.entity.name === '') { let package_file = this.get_package_file(code); struct = this.parse_doxy(package_file,"package"); } else { struct = this.parse_doxy(entity_file,"entity"); struct = this.parse_mermaid(entity_file,"entity"); struct = this.parse_ports_group(struct); struct = this.parse_virtual_bus(struct); struct.entity.description = this.normalize_description(struct.entity.description); } return struct; } async get_only_entity_name(code){ if (this.loaded === false){ return undefined; } let entity_declaration = this.get_entity_declaration(code); let entity_name = ''; if (entity_declaration !== undefined && entity_declaration.entity !== undefined){ entity_name = entity_declaration.entity.name; } return entity_name; } async get_only_package_name(code){ if (this.loaded === false){ return undefined; } let package_declaration = this.get_package_declaration(code); let package_name = ''; if (package_declaration !== undefined && package_declaration.package !== undefined){ package_name = package_declaration.package.name; } return package_name; } async get_entity_or_package_name(code){ let entity_name = await this.get_only_entity_name(code); if (entity_name === undefined || entity_name === ''){ let package_name = await this.get_only_package_name(code); return {name:package_name, type:'package'}; } else{ return {name:entity_name, type:'entity'}; } } get_entity_file(code) { try { let code_lines = code.split('\n'); let entity_declaration = this.get_entity_declaration(code); if (code_lines.length > 99999999999999) { let elements = { 'entity': entity_declaration.entity, 'generics': entity_declaration.generics, 'ports': entity_declaration.ports, 'body': { 'processes': [], 'instantiations': [] }, 'declarations': {'types': [], 'signals': [], 'constants': [], 'functions': []} }; return elements; } else { let elements = { 'entity': entity_declaration.entity, 'generics': entity_declaration.generics, 'ports': entity_declaration.ports, 'body': this.get_architecture_body_elements(code), 'declarations': this.get_declaration_elements('arch', code) }; return elements; } } catch (e) { return undefined; } } get_package_file(code) { try { let packge_declaration = this.get_package_declaration(code); let elements = { 'package': packge_declaration.package, 'declarations': this.get_declaration_elements('package', code) }; return elements; } catch (e) { return undefined; } } get_package_declaration(code) { let tree = this.parse(code); let description = ''; let name = ''; let break_p = false; let cursor = tree.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { cursor.gotoFirstChild(); do{ if (cursor.nodeType === 'package_declaration') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { name = cursor.nodeText; break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } else if (cursor.nodeType === 'comment') { description += this.get_comment(cursor.nodeText); } else { description = ''; } } while (cursor.gotoNextSibling() === true && break_p === false); } else if (cursor.nodeType === 'package_declaration'){ cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { name = cursor.nodeText; break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); let package_t = { 'name': name, 'description': description }; return { 'package': package_t, }; } get_entity_declaration(code) { let tree = this.parse(code); let entity_name = ''; let description = this.get_entity_declaration_description(code); let break_p = false; let cursor = tree.walk(); let result = { generics : [], ports: [] }; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'entity_declaration') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { entity_name = cursor.nodeText; } else if (cursor.nodeType === 'header') { result = this.get_generics_and_ports(cursor.currentNode()); } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); let entity = { 'name': entity_name, 'description': description }; return { 'entity': entity, 'generics': result.generics, 'ports': result.ports }; } get_entity_declaration_description(code) { let tree = this.parse(code); let entity_description = ''; let break_p = false; let cursor = tree.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'entity_declaration') { } else if (cursor.nodeType === 'comment') { entity_description += this.get_comment(cursor.nodeText); } } while (cursor.gotoNextSibling() === true && break_p === false); } else if (cursor.nodeType === 'comment') { entity_description += this.get_comment(cursor.nodeText); } else { entity_description = ''; } } while (cursor.gotoNextSibling() === true && break_p === false); return entity_description; } get_generics_and_ports(p) { let generics = []; let ports = []; let break_p = false; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'generic_clause') { generics = this.get_generics_or_ports(cursor.currentNode()); } else if (cursor.nodeType === 'port_clause') { ports = this.get_generics_or_ports(cursor.currentNode()); } } while (cursor.gotoNextSibling() === true && break_p === false); return { 'generics': generics, 'ports': ports }; } get_generics_or_ports(p) { let break_p = false; let elements_array = []; let cursor = p.walk(); let comments = ''; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'interface_constant_declaration') { let elements = this.get_element(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; elements_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'interface_signal_declaration') { let elements = this.get_element(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; elements_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'comment') { let txt_comment = cursor.nodeText.slice(2); let comment_line = cursor.startPosition.row; if (txt_comment[0] === this.comment_symbol || this.comment_symbol === '') { if (txt_comment.charAt(txt_comment.length - 1) === '\n' || txt_comment.charAt(txt_comment.length - 1) === '\r') { txt_comment = txt_comment.slice(0, -1); } let check = false; //Constants for (let i = 0; i < elements_array.length; ++i) { if (comment_line === elements_array[i].line) { if (this.comment_symbol === ''){ elements_array[i].description = txt_comment; } else{ elements_array[i].description = txt_comment.slice(1); } check = true; } } if (check === false) { comments += txt_comment.slice(1)+"\n"; } } } else { comments = ''; } } while (cursor.gotoNextSibling() === true && break_p === false); return elements_array; } get_element(p) { let break_p = false; let line = undefined; let elements_array = []; let cursor = p.walk(); let identifiers = []; let type = ''; let direction = ''; let default_value = ''; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier_list') { identifiers = this.get_identifier_of_list(cursor.currentNode()); line = cursor.startPosition.row; } else if (cursor.nodeType === 'subtype_indication') { type = cursor.nodeText; } else if (cursor.nodeType === 'mode') { direction = cursor.nodeText; } else if (cursor.nodeType === 'default_expression') { let normalized_value = cursor.nodeText; normalized_value = normalized_value.replace(':=', ''); normalized_value = normalized_value.replace(': =', ''); default_value = normalized_value.trim(); } } while (cursor.gotoNextSibling() === true && break_p === false); if (direction === undefined) { direction = ''; } for (let i = 0; i < identifiers.length; ++i) { let element = { 'name': identifiers[i], 'type': type, 'line': line, 'direction': direction.toLocaleLowerCase(), 'default_value': default_value }; elements_array.push(element); } return elements_array; } get_identifier_of_list(p) { let break_p = false; let identifiers = []; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { identifiers.push(cursor.nodeText); } } while (cursor.gotoNextSibling() === true && break_p === false); return identifiers; } get_architecture_body(code) { let tree = this.parse(code); let break_p = false; let counter = 0; let arch_body = undefined; let cursor = tree.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { counter += 1; if (counter === 2) { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'architecture_body') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'concurrent_statement_part') { arch_body = cursor.currentNode(); break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); } } } while (cursor.gotoNextSibling() === true && break_p === false); return arch_body; } get_architecture_body_elements(code) { try { let arch_body = this.get_architecture_body(code); let process_array = []; let instanciation_array = []; let cursor = arch_body.walk(); let comments = ''; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'component_instantiation_statement') { let elements = this.get_instantiation(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; instanciation_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'process_statement') { let elements = this.get_process(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments.replace('fsm_extract', ''); process_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'comment') { comments += this.get_comment(cursor.nodeText); } else { comments = ''; } } while (cursor.gotoNextSibling() !== false); return { 'processes': process_array, 'instantiations': instanciation_array }; } catch (e) { return { 'processes': [], 'instantiations': [] }; } } get_process(p) { let elements = []; let name = ''; let element = { 'name': '', 'type': '', 'sens_list': '' }; let break_p = false; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'label') { name = cursor.nodeText.replace(':', '').trim(); } else if (cursor.nodeType === 'sensitivity_list') { element.sens_list = cursor.nodeText.trim(); } } while (cursor.gotoNextSibling() === true && break_p === false); if (name === '') { element.name = 'unnamed'; } else { element.name = name; } elements.push(element); return elements; } get_instantiation(p) { let elements = []; let name = ''; let element = { 'name': '', 'type': '' }; let break_p = false; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'label') { name = cursor.nodeText.replace(':', '').trim(); } else if (cursor.nodeType === 'component_instantiation') { element.type = cursor.nodeText; } else if (cursor.nodeType === 'entity_instantiation') { const regex = /entity+[ \t]/gi; element.type = cursor.nodeText.replace(regex, ''); } } while (cursor.gotoNextSibling() === true && break_p === false); if (name === '') { element.name = 'unnamed'; } else { element.name = name; } elements.push(element); return elements; } get_architecture_declaration(code) { let tree = this.parse(code); let break_p = false; let counter = 0; let arch_declaration = undefined; let cursor = tree.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { counter += 1; if (counter === 2) { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'architecture_body') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'declarative_part') { arch_declaration = cursor.currentNode(); break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); } } } while (cursor.gotoNextSibling() === true && break_p === false); return arch_declaration; } get_package_top_declaration(code) { let tree = this.parse(code); let declaration = undefined; let break_p = false; let cursor = tree.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'design_unit') { cursor.gotoFirstChild(); do{ if (cursor.nodeType === 'package_declaration') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'declarative_part') { declaration = cursor.currentNode(); break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); } else if (cursor.nodeType === 'package_declaration'){ cursor.gotoFirstChild(); do { if (cursor.nodeType === 'declarative_part') { declaration = cursor.currentNode(); break_p = true; } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); return declaration; } get_declaration_elements(type, code) { let top_declaration; if (type === 'arch') { top_declaration = this.get_architecture_declaration(code); } else { top_declaration = this.get_package_top_declaration(code); } let types_array = []; let signals_array = []; let constants_array = []; let functions_array = []; if (top_declaration === undefined) { return { 'types': types_array, 'signals': signals_array, 'constants': constants_array, 'functions': functions_array }; } let cursor = top_declaration.walk(); let comments = ''; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'full_type_declaration') { let elements = this.get_full_type_declaration(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; types_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'signal_declaration') { let elements = this.get_signal_declaration(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; signals_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'subprogram_body' || cursor.nodeType === 'subprogram_declaration') { let elements = this.get_function_body(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; functions_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'constant_declaration') { let elements = this.get_constant_declaration(cursor.currentNode()); for (let i = 0; i < elements.length; ++i) { elements[i].description = comments; constants_array.push(elements[i]); } comments = ''; } else if (cursor.nodeType === 'comment') { let txt_comment = cursor.nodeText.slice(2); if (txt_comment.charAt(txt_comment.length - 1) === '\n' || txt_comment.charAt(txt_comment.length - 1) === '\r') { txt_comment = txt_comment.slice(0, -1); } let comment_line = cursor.startPosition.row; if (txt_comment[0] === this.comment_symbol || this.comment_symbol === '') { if (this.comment_symbol !== ''){ txt_comment = txt_comment.slice(1); } let check = false; //Types for (let i = 0; i < types_array.length; ++i) { if (comment_line === types_array[i].line) { types_array[i].description = txt_comment; check = true; } } //Signals for (let i = 0; i < signals_array.length; ++i) { if (comment_line === signals_array[i].line) { signals_array[i].description = txt_comment; check = true; } } //Constants for (let i = 0; i < constants_array.length; ++i) { if (comment_line === constants_array[i].line) { constants_array[i].description = txt_comment; check = true; } } //Functions for (let i = 0; i < functions_array.length; ++i) { if (comment_line === functions_array[i].line) { functions_array[i].description = txt_comment; check = true; } } if (check === false) { comments += txt_comment + "\n"; } } else { comments = ''; } } else { comments = ''; } } while (cursor.gotoNextSibling() !== false); return { 'types': types_array, 'signals': signals_array, 'constants': constants_array, 'functions': functions_array }; } remove_comments(str){ let str_split = str.split('\n'); let str_not_comments = ''; for (let i = 0; i < str_split.length; i++) { let element = str_split[i]; element = element.trim(); if (element[0] !== '-' && element[1] !== '-'){ str_not_comments += element + '\n'; } } return str_not_comments; } remove_break_line(str){ let str_out = str.replace(/(\r\n|\n|\r)/gm, " "); return str_out; } get_function_body(p) { let element = { 'name': '', 'line': 0, 'type': '', 'arguments': '', 'return': '' }; let break_p = false; let cursor = p.walk(); element.line = cursor.startPosition.row; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'pure_function_specification' || cursor.nodeType === 'procedure_specification') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { element.name = cursor.nodeText; } else if (cursor.nodeType === 'formal_procedure_parameter_clause' || cursor.nodeType === 'formal_function_parameter_clause'){ let return_arguments = cursor.nodeText; element.arguments = this.remove_break_line(this.remove_comments(return_arguments)); } else if (cursor.nodeType === 'return'){ let return_definitions = cursor.nodeText; element.return = this.remove_break_line(this.remove_comments(return_definitions)); } } while (cursor.gotoNextSibling() === true && break_p === false); } } while (cursor.gotoNextSibling() === true && break_p === false); return [element]; } get_full_type_declaration(p) { let elements = []; let element = { 'name': '', 'type': '', 'line': 0 }; let break_p = false; let cursor = p.walk(); element.line = cursor.startPosition.row; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier') { element.name = cursor.nodeText; } else if (cursor.nodeType === 'enumeration_type_definition') { let type_definition = cursor.nodeText; element.type = this.remove_break_line(this.remove_comments(type_definition)); } else if (cursor.nodeType === 'unbounded_array_definition') { let type_definition = cursor.nodeText; element.type = this.remove_break_line(this.remove_comments(type_definition)); } } while (cursor.gotoNextSibling() === true && break_p === false); elements.push(element); return elements; } get_signal_declaration(p) { let elements = []; let break_p = false; let cursor = p.walk(); let line = cursor.startPosition.row; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier_list') { let names = cursor.nodeText.split(','); for (let i = 0; i < names.length; ++i) { let element = { 'name': names[i], 'type': '', 'line': line }; elements.push(element); } } else if (cursor.nodeType === 'subtype_indication') { for (let i = 0; i < elements.length; ++i) { elements[i].type = cursor.nodeText; } } } while (cursor.gotoNextSibling() === true && break_p === false); return elements; } get_constant_declaration(p) { let elements = []; let break_p = false; let cursor = p.walk(); let line = cursor.startPosition.row; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'identifier_list') { let names = cursor.nodeText.split(','); for (let i = 0; i < names.length; ++i) { let element = { 'name': names[i], 'type': '', 'default_value': '', 'line': line }; elements.push(element); } } else if (cursor.nodeType === 'subtype_indication') { for (let i = 0; i < elements.length; ++i) { elements[i].type = cursor.nodeText; } } else if (cursor.nodeType === 'default_expression') { let normalized_value = cursor.nodeText; normalized_value = normalized_value.replace(':=', ''); normalized_value = normalized_value.replace(': =', ''); for (let i = 0; i < elements.length; ++i) { elements[i].default_value = normalized_value; } } } while (cursor.gotoNextSibling() === true && break_p === false); return elements; } } module.exports = { Parser: Parser };