@abaplint/core
Version:
abaplint - Core API
154 lines • 4.82 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlowGraph = exports.FLOW_EDGE_TYPE = void 0;
var FLOW_EDGE_TYPE;
(function (FLOW_EDGE_TYPE) {
FLOW_EDGE_TYPE["true"] = "true";
FLOW_EDGE_TYPE["false"] = "false";
FLOW_EDGE_TYPE["undefined"] = "undefined";
})(FLOW_EDGE_TYPE || (exports.FLOW_EDGE_TYPE = FLOW_EDGE_TYPE = {}));
class FlowGraph {
constructor(counter) {
this.edges = {};
this.label = "undefined";
this.startNode = "start#" + counter;
this.endNode = "end#" + counter;
}
getStart() {
return this.startNode;
}
getLabel() {
return this.label;
}
getEnd() {
return this.endNode;
}
addEdge(from, to, type) {
if (this.edges[from] === undefined) {
this.edges[from] = {};
}
this.edges[from][to] = type;
}
removeEdge(from, to) {
if (this.edges[from] === undefined) {
return;
}
delete this.edges[from][to];
if (Object.keys(this.edges[from]).length === 0) {
delete this.edges[from];
}
}
listEdges() {
const list = [];
for (const from of Object.keys(this.edges)) {
for (const to of Object.keys(this.edges[from])) {
list.push({ from, to, type: this.edges[from][to] });
}
}
return list;
}
listNodes() {
const set = new Set();
for (const l of this.listEdges()) {
set.add(l.from);
set.add(l.to);
}
return Array.from(set.values());
}
hasEdges() {
return Object.keys(this.edges).length > 0;
}
/** return value: end node of to graph */
addGraph(from, to, type) {
if (to.hasEdges() === false) {
return from;
}
this.addEdge(from, to.getStart(), type);
to.listEdges().forEach(e => this.addEdge(e.from, e.to, e.type));
return to.getEnd();
}
toJSON() {
return JSON.stringify(this.edges);
}
toTextEdges() {
let graph = "";
for (const l of this.listEdges()) {
const label = l.type === FLOW_EDGE_TYPE.undefined ? "" : ` [label="${l.type}"]`;
graph += `"${l.from}" -> "${l.to}"${label};\n`;
}
return graph.trim();
}
setLabel(label) {
this.label = label;
}
toDigraph() {
return `digraph G {
labelloc="t";
label="${this.label}";
graph [fontname = "helvetica"];
node [fontname = "helvetica", shape="box"];
edge [fontname = "helvetica"];
${this.toTextEdges()}
}`;
}
listSources(node) {
const set = [];
for (const l of this.listEdges()) {
if (node === l.to) {
set.push({ name: l.from, type: l.type });
}
}
return set;
}
listTargets(node) {
const set = [];
for (const l of this.listEdges()) {
if (node === l.from) {
set.push({ name: l.to, type: l.type });
}
}
return set;
}
/** removes all nodes containing "#" that have one in-going and one out-going edge */
reduce() {
for (const node of this.listNodes()) {
if (node.includes("#") === false) {
continue;
}
const sources = this.listSources(node);
const targets = this.listTargets(node);
if (sources.length > 0 && targets.length > 0) {
// hash node in the middle of the graph
for (const s of sources) {
this.removeEdge(s.name, node);
}
for (const t of targets) {
this.removeEdge(node, t.name);
}
for (const s of sources) {
for (const t of targets) {
let type = FLOW_EDGE_TYPE.undefined;
if (s.type !== FLOW_EDGE_TYPE.undefined) {
type = s.type;
}
if (t.type !== FLOW_EDGE_TYPE.undefined) {
if (type !== FLOW_EDGE_TYPE.undefined) {
throw new Error("reduce: cannot merge, different edge types");
}
type = t.type;
}
this.addEdge(s.name, t.name, type);
}
}
}
if (node.startsWith("end#") && sources.length === 0) {
for (const t of targets) {
this.removeEdge(node, t.name);
}
}
}
return this;
}
}
exports.FlowGraph = FlowGraph;
//# sourceMappingURL=flow_graph.js.map