1959 lines
77 KiB
JavaScript
1959 lines
77 KiB
JavaScript
const path = require('path');
|
|
|
|
async function get_svg_sm(language, code, comment_symbol) {
|
|
if (language === "vhdl") {
|
|
let parser = new Paser_fsm_vhdl(comment_symbol);
|
|
await parser.init();
|
|
let stm = await parser.get_svg_sm(code);
|
|
return stm;
|
|
}
|
|
else if (language === "verilog" || language === "systemverilog") {
|
|
let parser = new Paser_fsm_verilog(comment_symbol);
|
|
await parser.init();
|
|
let stm = await parser.get_svg_sm(code);
|
|
return stm;
|
|
}
|
|
}
|
|
module.exports = get_svg_sm;
|
|
|
|
class Ts_base_parser {
|
|
constructor() {
|
|
this.command_end_regex = /@end/gm;
|
|
}
|
|
|
|
search_multiple_in_tree(element, matching_title) {
|
|
var arr_match = [];
|
|
function recursive_searchTree(element, matching_title) {
|
|
let type = element.type;
|
|
if (type === matching_title) {
|
|
arr_match.push(element);
|
|
} else if (element !== null) {
|
|
var i;
|
|
var result = null;
|
|
for (i = 0; result === null && i < element.childCount; i++) {
|
|
result = recursive_searchTree(element.child(i), matching_title);
|
|
}
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
recursive_searchTree(element, matching_title);
|
|
return arr_match;
|
|
}
|
|
|
|
search_in_tree(element, matching_title) {
|
|
var match = undefined;
|
|
function recursive_searchTree(element, matching_title) {
|
|
let type = element.type;
|
|
if (type === matching_title) {
|
|
match = element;
|
|
} else if (element !== null) {
|
|
var i;
|
|
var result = null;
|
|
for (i = 0; result === null && i < element.childCount; i++) {
|
|
result = recursive_searchTree(element.child(i), matching_title);
|
|
if (result !== null) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
recursive_searchTree(element, matching_title);
|
|
return match;
|
|
}
|
|
|
|
get_item_multiple_from_childs(p, type) {
|
|
if (p === undefined) {
|
|
return [];
|
|
}
|
|
let items = [];
|
|
let cursor = p.walk();
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === type) {
|
|
let item = cursor.currentNode();
|
|
items.push(item);
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() === true);
|
|
return items;
|
|
}
|
|
|
|
get_item_from_childs(p, type) {
|
|
if (p === undefined) {
|
|
return undefined;
|
|
}
|
|
let item = undefined;
|
|
let cursor = p.walk();
|
|
let break_p = false;
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === type) {
|
|
item = cursor.currentNode();
|
|
break_p = true;
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() === true && break_p === false);
|
|
return item;
|
|
}
|
|
|
|
get_item_from_childs_last(p, type) {
|
|
if (p === undefined) {
|
|
return undefined;
|
|
}
|
|
let item = undefined;
|
|
let cursor = p.walk();
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === type) {
|
|
item = cursor.currentNode();
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() === true);
|
|
return item;
|
|
}
|
|
|
|
parse_doxy(dic, file_type) {
|
|
if (dic.info === undefined) {
|
|
dic.info = {};
|
|
}
|
|
// remove any spaces between linefeed and trim the string
|
|
let desc_root = dic[file_type];
|
|
// always remove carriage return
|
|
desc_root.description = desc_root.description.replace(/\r/gm, "");
|
|
// look for single line commands
|
|
const single_line_regex = /^\s*[@\\](file|date|title|custom_section_begin|custom_section_end)\s.+$/gm;
|
|
// get all matches for single line attributes
|
|
let matches_array = Array.from(desc_root.description.matchAll(single_line_regex));
|
|
// add a new property for the newly found matches
|
|
if (matches_array.length > 0) {
|
|
dic.info = {};
|
|
// append found matches
|
|
for (let index = 0; index < matches_array.length; index++) {
|
|
dic.info[matches_array[index][1]] = matches_array[index][0].replace(/^\s*[@\\](file|date|title|custom_section_begin|custom_section_end)/, "").trim();
|
|
}
|
|
// clean up the description field
|
|
desc_root.description = desc_root.description.replace(single_line_regex, "");
|
|
}
|
|
desc_root.description = desc_root.description.replace(/@copyright/gm, "\n@copyright");
|
|
desc_root.description = desc_root.description.replace(/@author/gm, "\n@author");
|
|
desc_root.description = desc_root.description.replace(/@version/gm, "\n@version");
|
|
desc_root.description = desc_root.description.replace(/@project/gm, "\n@project");
|
|
desc_root.description = desc_root.description.replace(/@brief/gm, "\n@brief");
|
|
desc_root.description = desc_root.description.replace(/@details/gm, "\n@details");
|
|
|
|
Doxygen_parser.parse_copyright(dic, desc_root);
|
|
Doxygen_parser.parse_author(dic, desc_root);
|
|
Doxygen_parser.parse_version(dic, desc_root);
|
|
Doxygen_parser.parse_project(dic, desc_root);
|
|
Doxygen_parser.parse_brief(dic, desc_root);
|
|
Doxygen_parser.parse_details(dic, desc_root);
|
|
return dic;
|
|
}
|
|
|
|
normalize_description(description) {
|
|
return description;
|
|
let desc_inst = description.replace(/\n\s*\n/g, '<br>');
|
|
desc_inst = desc_inst.replace(/\n/g, '');
|
|
return desc_inst;
|
|
}
|
|
|
|
get_comment(comment) {
|
|
if (comment === undefined) {
|
|
return '';
|
|
}
|
|
let txt_comment = comment.slice(2);
|
|
if (this.comment_symbol === '') {
|
|
return txt_comment;
|
|
}
|
|
else if (txt_comment[0] === this.comment_symbol) {
|
|
return txt_comment.slice(1);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
get_comment_with_break(comment) {
|
|
if (comment === undefined) {
|
|
return '';
|
|
}
|
|
let txt_comment = comment.slice(2);
|
|
if (this.comment_symbol === '') {
|
|
return txt_comment + '\n';
|
|
}
|
|
else if (txt_comment[0] === this.comment_symbol) {
|
|
return txt_comment.slice(1) + '\n';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
set_symbol(symbol) {
|
|
if (symbol === undefined) {
|
|
this.comment_symbol = '';
|
|
}
|
|
else {
|
|
this.comment_symbol = symbol;
|
|
}
|
|
}
|
|
|
|
parse_mermaid(dic, file_type) {
|
|
// the command regex
|
|
const mermaid_regex = /^\s*[@\\]mermaid\s*.*[@\\]end/gms;
|
|
// a variable to hold if a mermaid is found and currently opened
|
|
let mermaid_open = false;
|
|
// easy access to the entity description
|
|
let desc_root = dic[file_type];
|
|
// hold the mermaid data
|
|
let mermaid = "";
|
|
// always remove carriage return
|
|
desc_root.description = desc_root.description.replace(/\r/gm, "");
|
|
let match = desc_root.description.match(mermaid_regex)
|
|
if (match !== undefined && match !== null && match.length > 0) {
|
|
desc_root.description = desc_root.description.replace(match[0], "");
|
|
mermaid = match[0].replace(/[@\\]mermaid/gm, "")
|
|
mermaid = mermaid.replace(/[@\\]end/gm, "")
|
|
desc_root.description = desc_root.description.replace("\n\n", "")
|
|
dic[file_type]['description'] = desc_root.description
|
|
dic[file_type]['mermaid'] = mermaid;
|
|
}
|
|
return dic;
|
|
}
|
|
|
|
parse_ports_group(dic) {
|
|
|
|
const group_regex = /^\s*[@\\]portgroup\s.*$/gm;
|
|
let ports = dic.ports;
|
|
// hold the current group name
|
|
let group_name = "";
|
|
// flag to check if a group is open
|
|
let group_open = false;
|
|
// loop along all ports
|
|
for (let i = 0; i < ports.length; i++) {
|
|
let group = ports[i].description.match(group_regex);
|
|
// look for a new group name
|
|
if (group !== null && group.length > 0) {
|
|
group_open = true;
|
|
ports[i].description = ports[i].description.replace(/^\s*[@\\]portgroup\s/gm, "");
|
|
group_name = ports[i].description.match(/^\s*\w+/)[0];
|
|
ports[i].description = ports[i].description.replace(group_name, "");
|
|
}
|
|
|
|
ports[i].group = group_name;
|
|
}
|
|
dic.ports = ports;
|
|
return dic;
|
|
}
|
|
parse_virtual_bus(dic) {
|
|
const virtual_bus_regex_followed = /^\s*[@\\]virtualbus\s.*\n\n/gms;
|
|
const virtual_bus_regex_not_followed = /^\s*[@\\]virtualbus\s.*/
|
|
const virtual_bus_dir_regex = /^\s*[@\\]dir\s/gm;
|
|
const virtual_bus_keep_regex = /^\s*[@\\]keepports\s/gm
|
|
// the base struct is used to reset the virtual_bus_struct when needed
|
|
const virtual_bus_base_struct = {
|
|
"name": "",
|
|
"description": "",
|
|
"direction": "in",
|
|
"keep_ports": false,
|
|
"ports": []
|
|
}
|
|
let ports = dic.ports;
|
|
// hold the indexes that gets removed from the ports list
|
|
let ports_to_remove = [];
|
|
// holds the current virtual bus and gets filled when a new one is encountered
|
|
let virtual_bus_struct = clone(virtual_bus_base_struct);
|
|
// holds all the found virtual buses found so for
|
|
let virtual_bus_array = [];
|
|
// indicates if a virtual bus is found in a port or not
|
|
let virtual_bus_open = false;
|
|
// loop along all ports
|
|
for (let i = 0; i < ports.length; i++) {
|
|
// strip description from \r if present to deal with \n exclusively
|
|
ports[i].description = ports[i].description.replace(/\r/gm, "");
|
|
|
|
let virtual_bus = ports[i].description.match(virtual_bus_regex_followed);
|
|
|
|
if (virtual_bus === null) {
|
|
virtual_bus = ports[i].description.match(virtual_bus_regex_not_followed);
|
|
}
|
|
|
|
if (virtual_bus !== null) {
|
|
if (virtual_bus_open) {
|
|
// new virtual bus is found and another one was still open, add the old one to the array and clean it
|
|
virtual_bus_array.push(clone(virtual_bus_struct));
|
|
virtual_bus_struct = clone(virtual_bus_base_struct);
|
|
}
|
|
let virtual_bus_description = virtual_bus[0];
|
|
// clean the port description from the found virtual bus command
|
|
dic.ports[i].description = ports[i].description.replace(virtual_bus_regex_not_followed, "");
|
|
dic.ports[i].description = ports[i].description.replace(/\n\n/, "");
|
|
dic.ports[i].description = ports[i].description.replace(this.command_end_regex, "");
|
|
// strip virtual bus description from the command part
|
|
virtual_bus_description = virtual_bus_description.replace(/^\s*[@\\]virtualbus\s/, "");
|
|
// construct the name and description of virtual bus
|
|
let virtual_bus_name = virtual_bus_description.match(/^\s*\w+/);
|
|
if (virtual_bus_name !== null) {
|
|
virtual_bus_name = virtual_bus_description.match(/^\s*\w+/)[0];
|
|
}
|
|
else {
|
|
virtual_bus_name = "";
|
|
}
|
|
virtual_bus_description = virtual_bus_description.replace(virtual_bus_name, "");
|
|
let virtual_bus_dir = virtual_bus_description.match(virtual_bus_dir_regex);
|
|
// look for optional direction
|
|
if (virtual_bus_dir !== null && virtual_bus_dir.length > 0) {
|
|
virtual_bus_description = virtual_bus_description.replace(virtual_bus_dir[0], "");
|
|
virtual_bus_description = virtual_bus_description.replace(/\n\n/, "");
|
|
virtual_bus_dir = virtual_bus_description.match(/^\s*(out|in)/gm);
|
|
if (virtual_bus_dir !== null) {
|
|
virtual_bus_description = virtual_bus_description.replace(virtual_bus_dir[0], "");
|
|
virtual_bus_struct.direction = virtual_bus_dir[0].trim();
|
|
} else {
|
|
virtual_bus_struct.direction = "in";
|
|
}
|
|
}
|
|
// look for optional flag to keep in signals in table
|
|
let keep_ports = virtual_bus_description.match(virtual_bus_keep_regex);
|
|
// look for optional direction
|
|
if (keep_ports !== null && keep_ports.length > 0) {
|
|
virtual_bus_description = virtual_bus_description.replace(keep_ports[0], "");
|
|
virtual_bus_struct.keep_ports = true;
|
|
}
|
|
// update the virtual bus struct with the newly found fields
|
|
virtual_bus_struct.name = virtual_bus_name;
|
|
virtual_bus_struct.description = virtual_bus_description;
|
|
// keep the virtual bus opened to add incoming ports
|
|
virtual_bus_open = true;
|
|
}
|
|
|
|
if (virtual_bus_open) {
|
|
// copy the port to the newly created virtualbus
|
|
virtual_bus_struct.ports.push(clone(ports[i]));
|
|
// append current index to be removed
|
|
ports_to_remove.push(clone(i))
|
|
}
|
|
// remove any added \n to description
|
|
dic.ports[i].description = ports[i].description.replace(/\n/, "");
|
|
|
|
if (ports[i].description.match(this.command_end_regex) !== null) {
|
|
if (virtual_bus_open) {
|
|
virtual_bus_open = false;
|
|
for (let i = 0; i < virtual_bus_struct.ports.length; i++) {
|
|
virtual_bus_struct.ports[i].description = virtual_bus_struct.ports[i].description.replace(this.command_end_regex, "");
|
|
}
|
|
virtual_bus_array.push(clone(virtual_bus_struct));
|
|
virtual_bus_struct = clone(virtual_bus_base_struct);
|
|
}
|
|
}
|
|
}
|
|
if (virtual_bus_array.length > 0) {
|
|
// append the vbus to the json
|
|
dic.virtual_buses = virtual_bus_array;
|
|
// remove ports from the list
|
|
for (let index = 0; index < ports_to_remove.length; index++) {
|
|
const element = ports_to_remove[index];
|
|
dic.ports.splice(element - index, 1)
|
|
}
|
|
for (let index = 0; index < virtual_bus_array.length; index++) {
|
|
const element = virtual_bus_array[index];
|
|
dic.ports.push({
|
|
"name": element.name,
|
|
"type": "virtual_bus",
|
|
"line": -1,
|
|
"direction": element.direction,
|
|
"default_value": "",
|
|
"description": element.description,
|
|
"group": ""
|
|
});
|
|
}
|
|
}
|
|
return dic;
|
|
}
|
|
|
|
}
|
|
|
|
class Parser_fsm_base extends Ts_base_parser {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
check_empty_states_transitions(states) {
|
|
let check = true;
|
|
for (let i = 0; i < states.length; ++i) {
|
|
if (states[i].transitions.length !== 0) {
|
|
check = false;
|
|
}
|
|
}
|
|
return check;
|
|
}
|
|
|
|
check_stm(stm) {
|
|
let check = false;
|
|
let states = stm.states;
|
|
for (let i = 0; i < states.length; ++i) {
|
|
let transitions = states[i].transitions;
|
|
if (transitions.length > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return check;
|
|
}
|
|
|
|
json_to_svg(stm_json) {
|
|
let stmcat = this.get_smcat(stm_json);
|
|
const smcat = require("./state-machine-cat");
|
|
let svg;
|
|
try {
|
|
console.error = function () { };
|
|
svg = smcat.render(stmcat, { outputType: "svg" });
|
|
}
|
|
// eslint-disable-next-line no-empty
|
|
catch (e) { }
|
|
return svg;
|
|
}
|
|
|
|
get_smcat(stm_json) {
|
|
let sm_states = '';
|
|
let sm_transitions = '';
|
|
|
|
let states = stm_json.states;
|
|
let state_names = [];
|
|
for (let i = 0; i < states.length; ++i) {
|
|
if (states[i].transitions.length === 0) {
|
|
state_names.push(states[i].name);
|
|
}
|
|
}
|
|
let emptys = [];
|
|
for (let i = 0; i < state_names.length; ++i) {
|
|
let empty = true;
|
|
for (let j = 0; j < states.length; ++j) {
|
|
for (let m = 0; m < states[j].transitions.length; ++m) {
|
|
if (states[j].transitions[m].destination === state_names[i]) {
|
|
empty = false;
|
|
}
|
|
}
|
|
}
|
|
if (empty === true) {
|
|
emptys.push(state_names[i]);
|
|
}
|
|
}
|
|
|
|
let gosth = [];
|
|
state_names = [];
|
|
for (let i = 0; i < states.length; ++i) {
|
|
state_names.push(states[i].name);
|
|
}
|
|
for (let j = 0; j < states.length; ++j) {
|
|
for (let m = 0; m < states[j].transitions.length; ++m) {
|
|
if (state_names.includes(states[j].transitions[m].destination) === false) {
|
|
let element = { 'name': states[j].transitions[m].destination, 'transitions': [] };
|
|
stm_json.states.push(element);
|
|
gosth.push(states[j].transitions[m].destination);
|
|
}
|
|
}
|
|
}
|
|
let num_states = stm_json.states.length;
|
|
stm_json.states.forEach(function (i_state, i) {
|
|
let transitions = i_state.transitions;
|
|
let state_name = i_state.name;
|
|
if (emptys.includes(state_name) === true || gosth.includes(state_name) === true) {
|
|
sm_states += `${state_name} [color="red"]`;
|
|
}
|
|
else {
|
|
sm_states += `${state_name}`;
|
|
}
|
|
if (i !== num_states - 1) {
|
|
sm_states += ',';
|
|
}
|
|
else {
|
|
sm_states += ';\n';
|
|
}
|
|
if (gosth.includes(state_name) !== true) {
|
|
transitions.forEach(function (i_transition, j) {
|
|
if (gosth.includes(i_transition.destination) === true) {
|
|
sm_transitions +=
|
|
`${state_name} => ${i_transition.destination} [color="red"] : ${i_transition.condition};\n`;
|
|
}
|
|
else {
|
|
sm_transitions += `${state_name} => ${i_transition.destination} : ${i_transition.condition};\n`;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
let str_stm = stm_json.state_variable_name + "{\n" + sm_states + sm_transitions + "\n};";
|
|
return str_stm;
|
|
}
|
|
|
|
only_unique(value, index, self) {
|
|
return self.indexOf(value) === index;
|
|
}
|
|
|
|
get_comment(comment) {
|
|
if (comment === undefined) {
|
|
return '';
|
|
}
|
|
let txt_comment = comment.slice(2);
|
|
if (this.comment_symbol === '') {
|
|
return txt_comment + '\n';
|
|
}
|
|
else if (txt_comment[0] === this.comment_symbol) {
|
|
return txt_comment.slice(1).trim() + '\n';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
set_symbol(symbol) {
|
|
if (symbol === undefined) {
|
|
this.comment_symbol = '';
|
|
}
|
|
else {
|
|
this.comment_symbol = symbol;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class Paser_fsm_verilog extends Parser_fsm_base {
|
|
constructor(comment_symbol, parser) {
|
|
super();
|
|
this.set_symbol(comment_symbol);
|
|
if (parser !== undefined) {
|
|
this.parser = parser;
|
|
this.loaded_wasm = true;
|
|
}
|
|
}
|
|
|
|
set_comment_symbol(comment_symbol) {
|
|
this.set_symbol(comment_symbol);
|
|
}
|
|
|
|
async init() {
|
|
if (this.loaded_wasm !== true) {
|
|
try {
|
|
const Parser = require('./tree-sitter');
|
|
await Parser.init();
|
|
this.parser = new Parser();
|
|
let Lang = await Parser.Language.load(path.join(
|
|
path.dirname(__dirname),
|
|
path.sep + "resources" +
|
|
path.sep + "tree-sitter" +
|
|
path.sep + "tree-sitter-verilog.wasm"));
|
|
this.parser.setLanguage(Lang);
|
|
this.loaded_wasm = true;
|
|
}
|
|
catch (e) { }
|
|
}
|
|
}
|
|
|
|
async get_svg_sm(code, comment_symbol) {
|
|
this.set_symbol(comment_symbol);
|
|
let process;
|
|
try {
|
|
const 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 === 'module_or_generate_item') {
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === 'always_construct') {
|
|
let process = {
|
|
'code': this.get_deep_process(cursor.currentNode()),
|
|
'comments': comments.trim()
|
|
};
|
|
process_array.push(process);
|
|
comments = '';
|
|
}
|
|
else {
|
|
comments = '';
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() !== false);
|
|
cursor.gotoParent();
|
|
}
|
|
else if (cursor.nodeType === 'comment') {
|
|
comments += this.get_comment(cursor.nodeText);
|
|
}
|
|
else {
|
|
comments = '';
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() !== false);
|
|
return process_array;
|
|
}
|
|
|
|
get_deep_process(p) {
|
|
let statement = this.get_item_from_childs(p, 'statement');
|
|
let statement_item = this.get_item_from_childs(statement, 'statement_item');
|
|
let procedural_timing_control_statement =
|
|
this.get_item_from_childs(statement_item, 'procedural_timing_control_statement');
|
|
if (procedural_timing_control_statement === undefined) {
|
|
let seq_block = this.get_item_from_childs(statement_item, 'seq_block');
|
|
return seq_block;
|
|
}
|
|
let statement_or_null = this.get_item_from_childs(procedural_timing_control_statement, 'statement_or_null');
|
|
let statement_2 = this.get_item_from_childs(statement_or_null, 'statement');
|
|
let statement_item_2 = this.get_item_from_childs(statement_2, 'statement_item');
|
|
let seq_block = this.get_item_from_childs(statement_item_2, 'seq_block');
|
|
if (seq_block === undefined) {
|
|
let cond_statement = this.get_item_from_childs(statement_item_2, 'conditional_statement');
|
|
return cond_statement;
|
|
}
|
|
|
|
return seq_block;
|
|
}
|
|
|
|
get_architecture_body(p) {
|
|
let break_p = false;
|
|
let arch_body = undefined;
|
|
let cursor = p.walk();
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === 'module_declaration') {
|
|
arch_body = cursor.currentNode();
|
|
break_p = true;
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() === true && break_p === false);
|
|
return arch_body;
|
|
}
|
|
|
|
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
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
force_case_stm(p) {
|
|
let state_names = this.get_state_names_from_case(p);
|
|
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 };
|
|
}
|
|
|
|
get_state_names_from_case(p) {
|
|
let state_names = [];
|
|
let state_names_case = this.search_multiple_in_tree(p, 'case_item_expression');
|
|
for (let i = 0; i < state_names_case.length; ++i) {
|
|
state_names.push(state_names_case[i].text);
|
|
}
|
|
return state_names;
|
|
}
|
|
|
|
search_state_variable_candidates(p, state_names) {
|
|
let candidates = [];
|
|
let signals = this.search_multiple_in_tree(p, 'blocking_assignment');
|
|
for (let i = 0; i < signals.length; ++i) {
|
|
let rigth = this.get_rigth_simple_waveform_assignment(signals[i]);
|
|
if (rigth !== undefined) {
|
|
let left = this.get_left_simple_waveform_assignment(signals[i]);
|
|
if (state_names.includes(rigth) === true) {
|
|
candidates.push(left);
|
|
}
|
|
}
|
|
}
|
|
|
|
let variables = this.search_multiple_in_tree(p, 'nonblocking_assignment');
|
|
for (let i = 0; i < variables.length; ++i) {
|
|
let rigth = this.get_rigth_simple_variable_assignment(variables[i]);
|
|
if (rigth !== undefined) {
|
|
let left = this.get_rigth_simple_variable_assignment(variables[i]);
|
|
if (state_names.includes(rigth) === 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_states(p, state_variable_name) {
|
|
let case_items = this.get_item_multiple_from_childs(p, 'case_item');
|
|
let case_state = [];
|
|
for (let i = 0; i < case_items.length; ++i) {
|
|
let state = {
|
|
'name': '',
|
|
'transitions': [],
|
|
'start_position': [],
|
|
'end_position': []
|
|
};
|
|
let result = this.get_item_from_childs(case_items[i], 'case_item_expression');
|
|
if (result !== undefined && result.text !== 'default') {
|
|
state.name = result.text;
|
|
state.start_position = [result.startPosition.row, result.startPosition.column];
|
|
state.end_position = [result.endPosition.row, result.endPosition.column];
|
|
state.transitions = this.get_transitions(case_items[i], state_variable_name);
|
|
|
|
case_state.push(state);
|
|
}
|
|
}
|
|
return case_state;
|
|
}
|
|
|
|
get_transitions(p, state_variable_name, metacondition) {
|
|
let assign_transitions = [];
|
|
let if_transitions = [];
|
|
let last_transitions = [];
|
|
let transitions = [];
|
|
let skip = false;
|
|
let last = 0;
|
|
|
|
let statement_or_null;
|
|
if (p.type !== 'statement_or_null') {
|
|
statement_or_null = this.get_item_from_childs(p, 'statement_or_null');
|
|
}
|
|
else {
|
|
statement_or_null = p.walk().currentNode();
|
|
}
|
|
let statement = this.get_item_from_childs(statement_or_null, 'statement');
|
|
let statement_item = this.get_item_from_childs(statement, 'statement_item');
|
|
let seq_block = this.get_item_from_childs(statement_item, 'seq_block');
|
|
let itera_item = [];
|
|
if (seq_block === undefined) {
|
|
itera_item = [statement_item];
|
|
skip = true;
|
|
}
|
|
else {
|
|
itera_item = this.get_item_multiple_from_childs(seq_block, 'statement_or_null');
|
|
}
|
|
for (let i = 0; i < itera_item.length; ++i) {
|
|
let statement_item_2 = itera_item[i];
|
|
if (skip === false) {
|
|
let statement_2 = this.get_item_from_childs(itera_item[i], 'statement');
|
|
statement_item_2 = this.get_item_from_childs(statement_2, 'statement_item');
|
|
}
|
|
//Search if
|
|
let type;
|
|
let block;
|
|
let if_statement = this.get_item_from_childs(statement_item_2, 'conditional_statement');
|
|
if (if_statement === undefined) {
|
|
//Search assignment
|
|
let assign_statement = this.get_item_from_childs(statement_item_2, 'blocking_assignment');
|
|
if (assign_statement !== undefined) {
|
|
type = 'simple_waveform_assignment';
|
|
block = assign_statement;
|
|
}
|
|
else {
|
|
let nonassign_statement = this.get_item_from_childs(statement_item_2, 'nonblocking_assignment');
|
|
if (nonassign_statement !== undefined) {
|
|
type = 'simple_waveform_assignment';
|
|
block = nonassign_statement;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
type = 'if_statement';
|
|
block = if_statement;
|
|
}
|
|
|
|
if (type === 'if_statement') {
|
|
let tmp_transitions = this.get_if_transitions(block, state_variable_name, metacondition);
|
|
if_transitions = if_transitions.concat(tmp_transitions);
|
|
last = 0;
|
|
}
|
|
else if (type === 'simple_waveform_assignment') {
|
|
let tmp_transitions = this.get_assignament_transitions(block, state_variable_name, metacondition);
|
|
if (tmp_transitions.length !== 0 && tmp_transitions !== undefined) {
|
|
assign_transitions = tmp_transitions;
|
|
last_transitions = tmp_transitions;
|
|
last = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ifs = this.get_if_elsif_else(p);
|
|
//Set else condition
|
|
let conditions = [];
|
|
let else_condition = '';
|
|
for (let i = 0; i < ifs.length; ++i) {
|
|
let condition = ifs[i].condition;
|
|
if (condition !== '' && conditions.includes(condition) === false) {
|
|
else_condition += `not (${condition})\n`;
|
|
}
|
|
else {
|
|
let tmp_condition = else_condition.slice(0, -1);
|
|
//Remove duplicate conditions
|
|
let current_conditions = tmp_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;
|
|
ifs[i].condition = condition;
|
|
}
|
|
conditions.push(condition);
|
|
}
|
|
|
|
for (let i = 0; i < ifs.length; ++i) {
|
|
let transition = this.get_transition(ifs[i], state_variable_name, metacondition);
|
|
if (transition !== undefined) {
|
|
transitions = transitions.concat(transition);
|
|
}
|
|
}
|
|
return transitions;
|
|
}
|
|
|
|
get_if_elsif_else(p) {
|
|
let ifs = [];
|
|
let cursor = p.walk();
|
|
cursor.gotoFirstChild();
|
|
do {
|
|
if (cursor.nodeType === 'else') {
|
|
let break_p = false;
|
|
while (break_p === false && cursor.gotoNextSibling() !== false) {
|
|
if (cursor.nodeType === 'statement_or_null') {
|
|
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
|
|
let statement_item = this.get_item_from_childs(item, 'statement_item');
|
|
|
|
let block_item = this.get_item_from_childs(statement_item, 'seq_block');
|
|
if (block_item !== undefined) {
|
|
item = this.get_item_from_childs(block_item, 'statement_or_null');
|
|
item = this.get_item_from_childs(item, 'statement');
|
|
statement_item = this.get_item_from_childs(item, 'statement_item');
|
|
}
|
|
item = this.get_item_from_childs(statement_item, 'conditional_statement');
|
|
if (item !== undefined) {
|
|
let tmp_ifs = this.get_if_elsif_else(item);
|
|
ifs = ifs.concat(tmp_ifs);
|
|
}
|
|
else {
|
|
let if_item_else = {
|
|
'condition': '',
|
|
'code': '',
|
|
'start_position': '',
|
|
'end_position': ''
|
|
};
|
|
|
|
let blocking_assignment = this.get_item_from_childs(statement_item, 'blocking_assignment');
|
|
if (blocking_assignment !== undefined) {
|
|
if (block_item !== undefined) {
|
|
if_item_else.code = block_item;
|
|
// if_item_else.start_position = start_position;
|
|
// if_item_else.end_position = end_position;
|
|
}
|
|
else {
|
|
if_item_else.code = statement_item;
|
|
// if_item_else.start_position = start_position;
|
|
// if_item_else.end_position = end_position;
|
|
}
|
|
ifs.push(if_item_else);
|
|
}
|
|
else {
|
|
let nonblocking_assignment = this.get_item_from_childs(statement_item, 'nonblocking_assignment');
|
|
if (nonblocking_assignment !== undefined) {
|
|
if (block_item !== undefined) {
|
|
if_item_else.code = block_item;
|
|
// if_item_else.start_position = start_position;
|
|
// if_item_else.end_position = end_position;
|
|
}
|
|
else {
|
|
if_item_else.code = statement_item;
|
|
// if_item_else.start_position = start_position;
|
|
// if_item_else.end_position = end_position;
|
|
}
|
|
ifs.push(if_item_else);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (cursor.nodeType === 'if') {
|
|
let break_p = false;
|
|
let if_item = {
|
|
'condition': '',
|
|
'code': ''
|
|
};
|
|
while (break_p === false && cursor.gotoNextSibling() !== false) {
|
|
if (cursor.nodeType === 'cond_predicate') {
|
|
let item = this.get_item_from_childs(cursor.currentNode(), 'expression_or_cond_pattern');
|
|
if (item !== undefined) {
|
|
if_item.condition = item.text;
|
|
if_item.start_position = item.startPosition;
|
|
if_item.end_position = item.endPosition;
|
|
}
|
|
}
|
|
else if (cursor.nodeType === 'statement_or_null') {
|
|
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
|
|
item = this.get_item_from_childs(item, 'statement_item');
|
|
if (this.get_item_from_childs(item, 'seq_block') !== undefined) {
|
|
item = this.get_item_from_childs(item, 'seq_block');
|
|
if_item.start_position = item.startPosition;
|
|
if_item.end_position = item.endPosition;
|
|
// item = this.get_item_from_childs(item, 'statement_or_null');
|
|
// item = this.get_item_from_childs(item, 'statement');
|
|
// item = this.get_item_from_childs(item, 'statement_item');
|
|
}
|
|
if_item.code = item;
|
|
break_p = true;
|
|
ifs.push(if_item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (cursor.gotoNextSibling() !== false);
|
|
return ifs;
|
|
}
|
|
|
|
get_assignament_transitions(p, state_variable_name, metacondition) {
|
|
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 condition = '';
|
|
if (metacondition !== '' && metacondition !== undefined) {
|
|
condition = metacondition;
|
|
}
|
|
|
|
let destination = tmp_destination;
|
|
let transition = {
|
|
'condition': condition,
|
|
'destination': destination,
|
|
'start_position': start_position,
|
|
'end_position': end_position
|
|
};
|
|
transitions.push(transition);
|
|
}
|
|
return transitions;
|
|
}
|
|
|
|
get_transition(p, state_variable_name, metacondition) {
|
|
let condition = p.condition;
|
|
let tmp_start_position = p.start_position;
|
|
let tmp_end_position = p.end_position;
|
|
|
|
let start_position = [tmp_start_position.row, tmp_start_position.column];
|
|
let end_position = [tmp_end_position.row, tmp_end_position.column];
|
|
let transitions = this.get_transitions_in_if(p.code, state_variable_name,
|
|
condition, start_position, end_position, metacondition);
|
|
return transitions;
|
|
}
|
|
|
|
get_start_position_array(p) {
|
|
let tmp_position = p.code.startPosition;
|
|
return tmp_position;
|
|
}
|
|
|
|
get_end_position_array(p) {
|
|
let tmp_position = p.code.endPosition;
|
|
return tmp_position;
|
|
}
|
|
|
|
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 === 'blocking_assignment' || cursor.nodeType === 'nonblocking_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 === 'conditional_statement') {
|
|
last = 0;
|
|
let if_transitions_tmp = this.get_if_transitions(cursor.currentNode(), state_variable_name, condition);
|
|
if_transitions = if_transitions.concat(if_transitions_tmp);
|
|
}
|
|
else if (cursor.nodeType === 'statement_or_null') {
|
|
last = 0;
|
|
|
|
//check assignement
|
|
let item = this.get_item_from_childs(cursor.currentNode(), 'statement');
|
|
item = this.get_item_from_childs(item, 'statement_item');
|
|
let item_0 = this.get_item_from_childs(item, 'blocking_assignment');
|
|
let item_1 = this.get_item_from_childs(item, 'nonblocking_assignment');
|
|
let if_item = true;
|
|
if (item_0 !== undefined || item_1 !== undefined) {
|
|
if_item = false;
|
|
}
|
|
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;
|
|
}
|
|
|
|
let if_transitions_tmp = [];
|
|
//check block if
|
|
let item_block_if = this.get_item_from_childs(item, 'conditional_statement');
|
|
if (item_block_if === undefined) {
|
|
if_transitions_tmp = this.get_transitions(cursor.currentNode(), state_variable_name, condition);
|
|
}
|
|
else {
|
|
if_transitions_tmp = this.get_if_transitions(item_block_if, state_variable_name, condition);
|
|
}
|
|
|
|
if (if_item === false) {
|
|
if (if_transitions_tmp.length !== 0) {
|
|
assign_transitions = if_transitions_tmp;
|
|
last_transitions = if_transitions_tmp;
|
|
}
|
|
}
|
|
else {
|
|
if_transitions = if_transitions.concat(if_transitions_tmp);
|
|
}
|
|
|
|
}
|
|
}
|
|
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;
|
|
let left = this.get_left_simple_waveform_assignment(p);
|
|
if (left === state_variable_name) {
|
|
destination = this.get_rigth_simple_waveform_assignment(p);
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
check_get_simple_variable_assignment(p, state_variable_name) {
|
|
let destination = undefined;
|
|
let left = this.get_left_simple_waveform_assignment(p);
|
|
if (left === state_variable_name) {
|
|
destination = this.get_rigth_simple_variable_assignment(p);
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
get_left_simple_waveform_assignment(p) {
|
|
let left = '';
|
|
let item = this.get_item_from_childs(p, 'operator_assignment');
|
|
item = this.get_item_from_childs(item, 'variable_lvalue');
|
|
if (item !== undefined) {
|
|
left = item.text;
|
|
}
|
|
if (left === '') {
|
|
item = this.get_item_from_childs(p, 'variable_lvalue');
|
|
if (item !== undefined) {
|
|
left = item.text;
|
|
}
|
|
}
|
|
return left;
|
|
}
|
|
|
|
get_rigth_simple_waveform_assignment(p) {
|
|
let rigth = undefined;
|
|
let item = this.get_item_from_childs(p, 'operator_assignment');
|
|
item = this.get_item_from_childs(item, 'expression');
|
|
if (item !== undefined) {
|
|
rigth = item.text;
|
|
}
|
|
if (rigth === undefined) {
|
|
item = this.get_item_from_childs(p, 'expression');
|
|
if (item !== undefined) {
|
|
rigth = item.text;
|
|
}
|
|
}
|
|
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_state_variable_name(p) {
|
|
let state_variable_name = undefined;
|
|
let case_expression = this.get_item_from_childs(p, 'case_expression');
|
|
if (case_expression !== undefined) {
|
|
state_variable_name = case_expression.text;
|
|
}
|
|
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_txt = '';
|
|
let label = this.get_item_from_childs(p, "block_identifier");
|
|
if (label === undefined) {
|
|
label_txt = ''
|
|
}
|
|
else {
|
|
label_txt = label.text;
|
|
}
|
|
return label_txt;
|
|
}
|
|
}
|
|
|
|
class Paser_fsm_vhdl extends Parser_fsm_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('./tree-sitter');
|
|
await Parser.init();
|
|
this.parser = new Parser();
|
|
let Lang = await
|
|
Parser.Language.load(path.join(
|
|
path.dirname(__dirname),
|
|
path.sep + "resources" +
|
|
path.sep + "tree-sitter" +
|
|
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.split(/(\s)/)[0].trim();
|
|
}
|
|
}
|
|
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.split(/(\s)/)[0].trim();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|