UNPKG

teroshdl

Version:

Backend for terosHDL IDE

712 lines (666 loc) 28.4 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 stm_base = require('./stm_base_parser'); class Paser_stm_vhdl extends stm_base.Parser_stm_base { constructor(comment_symbol, parser) { super(); this.set_symbol(comment_symbol); if (parser !== undefined) { this.parser = parser; this.loaded_wasm = true; } } async init() { if (this.loaded_wasm !== true) { try { 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_wasm = true; } catch (e) {} } } set_comment_symbol(comment_symbol) { this.set_symbol(comment_symbol); } async get_svg_sm(code, comment_symbol) { this.set_comment_symbol(comment_symbol); let process; let tree; try { tree = this.parser.parse(code); process = this.get_process(tree); } catch (e) { return { 'svg': [], 'stm': [] }; } let stm = []; let svg = []; for (let i = 0; i < process.length; ++i) { let states; try { states = this.get_process_info(process[i]); } catch (e) { states = undefined; } if (states !== undefined) { for (let j = 0; j < states.length; ++j) { if (this.check_stm(states[j]) === true) { stm.push(states[j]); let svg_tmp = this.json_to_svg(states[j]); let stm_tmp = { 'svg': svg_tmp, 'description': states[j].description }; svg.push(stm_tmp); } } } } return { 'svg': svg, 'stm': stm }; } get_process(tree) { let process_array = []; let arch_body = this.get_architecture_body(tree); let cursor = arch_body.walk(); let comments = ''; // Process cursor.gotoFirstChild(); do { if (cursor.nodeType === 'process_statement') { let process = { 'code': cursor.currentNode(), 'comments': comments }; process_array.push(process); comments = ''; } else if (cursor.nodeType === 'comment') { comments += this.get_comment(cursor.nodeText); } else { comments = ''; } } while (cursor.gotoNextSibling() !== false); return process_array; } get_architecture_body(p) { let cursor = p.walk(); let item = this.get_item_multiple_from_childs(cursor.currentNode(), 'design_unit'); if (item.length === 2) { item = this.get_item_from_childs(item[1], 'architecture_body'); item = this.get_item_from_childs(item, 'concurrent_statement_part'); return item; } else { return undefined; } } get_process_info(proc) { let stms = []; let p = proc.code; let name = this.get_process_label(p); let case_statements = this.get_case_process(p); for (let i = 0; i < case_statements.length; ++i) { let description = proc.comments; let p_info = { 'description': description.replace('fsm_extract', ''), 'name': '', 'state_variable_name': '', 'states': [] }; p_info.name = name; if (case_statements !== undefined && case_statements.length !== 0) { p_info.state_variable_name = this.get_state_variable_name(case_statements[i]); p_info.states = this.get_states(case_statements[i], p_info.state_variable_name); let check = this.check_empty_states_transitions(p_info.states); if (check === true) { let result = this.force_case_stm(case_statements[i]); p_info.state_variable_name = result.variable_name; p_info.states = result.states; } stms.push(p_info); } } return stms; } force_case_stm(p) { let state_names = this.get_state_names_from_case(p).map(v => v.toLowerCase()); let state_name_candidate = this.search_state_variable_candidates(p, state_names); let states = this.get_states(p, state_name_candidate); return { 'variable_name': state_name_candidate, 'states': states }; } search_state_variable_candidates(p, state_names) { let candidates = []; let signals = this.search_multiple_in_tree(p, 'simple_waveform_assignment'); for (let i = 0; i < signals.length; ++i) { let rigth = this.get_item_from_childs(signals[i], 'waveforms'); if (rigth !== undefined) { let rigth_text = rigth.text.toLowerCase(); let left = this.get_left_simple_waveform_assignment(signals[i]); if (state_names.includes(rigth_text) === true) { candidates.push(left); } } } let variables = this.search_multiple_in_tree(p, 'simple_variable_assignment'); for (let i = 0; i < variables.length; ++i) { let rigth = this.get_item_from_childs(variables[i], 'waveforms'); if (rigth === undefined) { rigth = this.get_item_from_childs_last(variables[i], 'simple_name'); } if (rigth !== undefined) { let rigth_text = rigth.text.toLowerCase(); let left = this.get_left_simple_waveform_assignment(variables[i]); if (state_names.includes(rigth_text) === true) { candidates.push(left); } } } let unique = this.mode(candidates); return unique; } mode(array) { if (array.length === 0){ return null; } var mode_map = {}; var max_el = array[0], max_count = 1; for (var i = 0; i < array.length; i++) { var el = array[i]; if (mode_map[el] == null) { mode_map[el] = 1; } else { mode_map[el]++; } if (mode_map[el] > max_count) { max_el = el; max_count = mode_map[el]; } } return max_el; } get_state_names_from_case(p) { let state_names = []; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'case_statement_alternative') { let result = this.get_state_name(cursor.currentNode()); let name = result.state_name; state_names.push(name); } } while (cursor.gotoNextSibling() !== false); return state_names; } get_states(p, state_variable_name) { let case_state = []; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'case_statement_alternative') { let state = { 'name': '', 'transitions': [], 'start_position': [], 'end_position': [] }; let result = this.get_state_name(cursor.currentNode()); let name = result.state_name; if (name !== undefined && name.toLocaleLowerCase() !== 'others') { state.name = result.state_name; state.start_position = result.start_position; state.end_position = result.end_position; state.transitions = this.get_transitions(cursor.currentNode(), state_variable_name); case_state.push(state); } } } while (cursor.gotoNextSibling() !== false); return case_state; } get_transitions(p, state_variable_name) { let transitions = []; let cursor = p.walk(); let last = 0; let last_transitions = []; //if transitions let if_transitions = []; //assign transitions let assign_transitions = []; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'sequence_of_statements') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'if_statement') { let tmp_transitions = this.get_if_transitions(cursor.currentNode(), state_variable_name); if_transitions = if_transitions.concat(tmp_transitions); last = 0; } else if (cursor.nodeType === 'simple_waveform_assignment') { let tmp_transitions = this.get_assignament_transitions( cursor.currentNode(), state_variable_name); if (tmp_transitions.length !== 0 && tmp_transitions !== undefined) { assign_transitions = tmp_transitions; last_transitions = tmp_transitions; last = 1; } } else if (cursor.nodeType === 'simple_variable_assignment') { let tmp_transitions = this.get_assignament_variable_transitions( cursor.currentNode(), state_variable_name); if (tmp_transitions.length !== 0 && tmp_transitions !== undefined) { assign_transitions = tmp_transitions; last_transitions = tmp_transitions; last = 1; } } else if (cursor.nodeType === 'case_statement') { let tmp_transitions = this.get_case_transitions(cursor.currentNode(), state_variable_name); if_transitions = if_transitions.concat(tmp_transitions); last = 0; } } while (cursor.gotoNextSibling() !== false); } } while (cursor.gotoNextSibling() !== false); if (last === 1) { transitions = last_transitions; } else { transitions = if_transitions.concat(assign_transitions); } return transitions; } get_if_transitions(p, state_variable_name, metacondition) { let transitions = []; let cursor = p.walk(); let else_conditions = ''; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'elsif' || cursor.nodeType === 'if') { let if_condition = this.get_condition(cursor.currentNode()); if (if_condition !== undefined) { else_conditions += `not (${if_condition.condition})\n`; } let transition = this.get_transition(cursor.currentNode(), state_variable_name, metacondition); if (transition !== undefined) { transitions = transitions.concat(transition); } } else if (cursor.nodeType === 'else') { if (metacondition !== undefined) { else_conditions = metacondition + '\n' + else_conditions; } let transition = this.get_transition(cursor.currentNode(), state_variable_name, else_conditions); if (transition !== undefined) { transitions = transitions.concat(transition); } } } while (cursor.gotoNextSibling() !== false); return transitions; } get_case_transitions(p, state_variable_name, metacondition) { let transitions = []; let cursor = p.walk(); let else_conditions = ''; let case_switch = this.get_item_from_childs(cursor.currentNode(), 'simple_name').text; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'case_statement_alternative') { let choice = this.get_item_from_childs(cursor.currentNode(), 'choices'); let choice_txt = choice.text; let if_condition = `${case_switch} = ${choice_txt}`; if (choice_txt.toLocaleLowerCase() === 'others') { if_condition = else_conditions; } else if (if_condition !== undefined) { else_conditions += `not (${if_condition})\n`; } let transition = this.get_transition(cursor.currentNode(), state_variable_name, metacondition, if_condition); if (transition !== undefined) { transitions = transitions.concat(transition); } } } while (cursor.gotoNextSibling() !== false); return transitions; } get_assignament_transitions(p, state_variable_name) { let transitions = []; let tmp_destination = this.check_get_simple_waveform_assignment(p, state_variable_name); if (tmp_destination !== undefined) { let s_position = p.startPosition; let e_position = p.endPosition; let start_position = [s_position.row, e_position.column - 1]; let end_position = [e_position.row, e_position.column]; let destination = tmp_destination; let transition = { 'condition': '', 'destination': destination, 'start_position': start_position, 'end_position': end_position }; transitions.push(transition); } return transitions; } get_assignament_variable_transitions(p, state_variable_name) { let transitions = []; let tmp_destination = this.check_get_simple_variable_assignment(p, state_variable_name); if (tmp_destination !== undefined) { let s_position = p.startPosition; let e_position = p.endPosition; let start_position = [s_position.row, e_position.column - 1]; let end_position = [e_position.row, e_position.column]; let destination = tmp_destination; let transition = { 'condition': '', 'destination': destination, 'start_position': start_position, 'end_position': end_position }; transitions.push(transition); } return transitions; } get_transition(p, state_variable_name, metacondition, choice) { let result = this.get_condition(p, choice); let condition = result.condition; let start_position = result.start_position; let end_position = result.end_position; let transitions = this.get_transitions_in_if(p, state_variable_name, condition, start_position, end_position, metacondition); return transitions; } get_transitions_in_if(p, state_variable_name, condition, start_position, end_position, metacondition) { let last = 0; let last_transitions = []; //if transitions let if_transitions = []; //assign transitions let assign_transitions = []; let transitions = []; let destination = undefined; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'sequence_of_statements') { cursor.gotoFirstChild(); do { if (cursor.nodeType === 'simple_waveform_assignment') { let tmp_destination = this.check_get_simple_waveform_assignment( cursor.currentNode(), state_variable_name); if (tmp_destination !== undefined) { destination = tmp_destination; if (condition !== undefined && destination !== undefined) { let transition = { 'condition': '', 'destination': '', 'start_position': start_position, 'end_position': end_position }; if (metacondition !== undefined && metacondition !== '') { condition += `\n${metacondition}`; let current_conditions = condition.split('\n'); let unique = current_conditions.filter(this.only_unique); let condition_tmp = ''; for (let i = 0; i < unique.length - 1; ++i) { condition_tmp += unique[i] + '\n'; } condition_tmp += unique[unique.length - 1] + '\n'; condition = condition_tmp; } transition.condition = condition; transition.destination = destination; last = 1; assign_transitions = [transition]; last_transitions = [transition]; } } } else if (cursor.nodeType === 'simple_variable_assignment') { let tmp_destination = this.check_get_simple_variable_assignment( cursor.currentNode(), state_variable_name); if (tmp_destination !== undefined) { destination = tmp_destination; if (condition !== undefined && destination !== undefined) { let transition = { 'condition': '', 'destination': '', 'start_position': start_position, 'end_position': end_position }; if (metacondition !== undefined && metacondition !== '') { condition += `\n${metacondition}`; let current_conditions = condition.split('\n'); let unique = current_conditions.filter(this.only_unique); let condition_tmp = ''; for (let i = 0; i < unique.length - 1; ++i) { condition_tmp += unique[i] + '\n'; } condition_tmp += unique[unique.length - 1] + '\n'; condition = condition_tmp; } transition.condition = condition; transition.destination = destination; last = 1; assign_transitions = [transition]; last_transitions = [transition]; } } } else if (cursor.nodeType === 'if_statement') { if (metacondition !== undefined && metacondition !== '') { condition += condition + '\n' + metacondition; } last = 0; if_transitions = this.get_if_transitions(cursor.currentNode(), state_variable_name, condition); } else if (cursor.nodeType === 'case_statement') { if (metacondition !== undefined && metacondition !== '') { condition += condition + '\n' + metacondition; } last = 0; if_transitions = this.get_case_transitions(cursor.currentNode(), state_variable_name, condition); } } while (cursor.gotoNextSibling() !== false); } } while (cursor.gotoNextSibling() !== false); if (last !== 0) { transitions = last_transitions; } else { transitions = if_transitions.concat(assign_transitions); } return transitions; } check_get_simple_waveform_assignment(p, state_variable_name) { let destination = undefined; if (state_variable_name === undefined) { return destination; } if (this.get_left_simple_waveform_assignment(p).toLowerCase() === state_variable_name.toLowerCase()) { destination = this.get_rigth_simple_waveform_assignment(p); } return destination; } check_get_simple_variable_assignment(p, state_variable_name) { let destination = undefined; if (state_variable_name === undefined) { return destination; } if (this.get_left_simple_waveform_assignment(p).toLowerCase() === state_variable_name.toLowerCase()) { destination = this.get_rigth_simple_variable_assignment(p); } return destination; } get_left_simple_waveform_assignment(p) { let left = 'undefined'; let cursor = p.walk(); let break_p = false; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'simple_name') { left = cursor.nodeText; break_p = true; } else if (cursor.nodeType === 'selected_name') { left = cursor.nodeText.split('.'); left = left[left.length - 1]; break_p = true; } } while (cursor.gotoNextSibling() !== false && break_p === false); return left; } get_rigth_simple_waveform_assignment(p) { let rigth = undefined; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'waveforms') { rigth = cursor.nodeText; } } while (cursor.gotoNextSibling() !== false); return rigth; } get_rigth_simple_variable_assignment(p) { let rigth = undefined; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'simple_name') { rigth = cursor.nodeText; } } while (cursor.gotoNextSibling() !== false); return rigth; } get_condition(p, choice) { let condition = ''; let cursor = p.walk(); let start_position = []; let end_position = []; let s_position = cursor.startPosition; let e_position = cursor.endPosition; start_position = [s_position.row, s_position.column]; end_position = [e_position.row, e_position.column]; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'relation' || cursor.nodeType === 'logical_expression' || cursor.nodeType === 'parenthesized_expression') { if (cursor.nodeType === 'parenthesized_expression') { condition = this.get_relation_of_parenthesized_expression(cursor.currentNode()); } else { condition = cursor.nodeText; } s_position = cursor.startPosition; e_position = cursor.endPosition; start_position = [s_position.row, s_position.column]; end_position = [e_position.row, e_position.column]; } else if (cursor.nodeType === 'choices') { condition = choice; s_position = cursor.startPosition; e_position = cursor.endPosition; start_position = [s_position.row, s_position.column]; end_position = [e_position.row, e_position.column]; } if (cursor.nodeType === 'else') { s_position = cursor.startPosition; e_position = cursor.endPosition; start_position = [s_position.row, s_position.column]; end_position = [e_position.row, e_position.column]; } } while (cursor.gotoNextSibling() !== false); return { 'condition': condition, 'start_position': start_position, 'end_position': end_position }; } get_relation_of_parenthesized_expression(p) { let relation = undefined; let cursor = p.walk(); let break_p = false; cursor.gotoFirstChild(); do { if (cursor.nodeType === 'relation' || cursor.nodeType === 'logical_expression') { relation = cursor.nodeText; break_p = true; } } while (cursor.gotoNextSibling() !== false && break_p === false); return relation; } get_state_name(p) { let state_name = undefined; let start_position = []; let end_position = []; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'choices') { let s_position = cursor.startPosition; let e_position = cursor.endPosition; start_position = [s_position.row, s_position.column]; end_position = [e_position.row, e_position.column]; state_name = cursor.nodeText; } } while (cursor.gotoNextSibling() !== false); return { 'state_name': state_name, 'start_position': start_position, 'end_position': end_position }; } get_state_variable_name(p) { let state_variable_name = undefined; let cursor = p.walk(); cursor.gotoFirstChild(); do { if (cursor.nodeType === 'simple_name') { state_variable_name = cursor.nodeText; } else if (cursor.nodeType === 'parenthesized_expression') { state_variable_name = cursor.nodeText.replace('(', '').replace(')', ''); } } while (cursor.gotoNextSibling() !== false); return state_variable_name; } get_case_process(p) { let case_statement = this.search_multiple_in_tree(p, 'case_statement'); return case_statement; } get_process_label(p) { let label = ''; let cursor = p.walk(); //Process label cursor.gotoFirstChild(); if (cursor.nodeType === 'label') { cursor.gotoFirstChild(); label = cursor.nodeText; } return label; } } module.exports = { Paser_stm_vhdl: Paser_stm_vhdl };