-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.js
129 lines (107 loc) · 3.26 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
module.exports = load;
var parseDot = require('dotparser');
/**
* Loads graph from 'dot' string.
*
* @param {String} dotGraph a graph in `dot` format
* @param {ngraph.graph=} appendTo optional argument if you want to append parsed
* graph to an old graph.
*
* @return {ngraph.graph} Parsed graph
*
* @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
* @see https://github.com/anvaka/ngraph.graph
*/
function load(dotGraph, appendTo) {
var dotAST = parseDot(dotGraph);
if (dotAST.length > 1 && appendTo !== undefined) {
throw new Error('Dot file contains multiple graphs. Cannot use `saveTo` in this case');
}
if (!appendTo) {
appendTo = require('ngraph.graph')();
}
// by default load will only load the first graph:
return loadOne(appendTo, dotAST[0]);
}
function loadOne(graph, ast) {
loadSubgraph(graph, ast);
return graph;
}
function loadSubgraph(graph, ast) {
var children = ast.children;
if (!children) return graph;
var addedNodes = [];
for (var i = 0; i < children.length; ++i) {
var child = children[i];
if (child.type === 'edge_stmt') {
concat(addedNodes, processEdgeStatement(graph, child));
} else if (child.type === 'node_stmt') {
concat(addedNodes, processNodeStatement(graph, child));
} else if (child.type === 'subgraph') {
concat(addedNodes, loadSubgraph(graph, child));
}
}
return addedNodes;
}
function processEdgeStatement(graph, edgeAST) {
var edges = edgeAST.edge_list;
if (edges.length === 0) return; // wat? should I throw?
var first = edges[0];
var addedNodes = [];
var prevNode = addNode(graph, first);
concat(addedNodes, prevNode);
var attributes = parseAttributesAsData(edgeAST.attr_list);
for (var i = 1; i < edges.length; ++i) {
var nextNode = addNode(graph, edges[i]);
concat(addedNodes, nextNode);
addLink(graph, prevNode, nextNode, attributes);
prevNode = nextNode;
}
return addedNodes;
}
function processNodeStatement(graph, nodeStatement) {
return addNode(graph, nodeStatement.node_id, nodeStatement.attr_list);
}
function concat(head, tail) {
for (var i = 0; i < tail.length; ++i) {
head.push(tail[i]);
}
return head;
}
function addNode(graph, nodeAST, attributesList) {
if (nodeAST.type === 'node_id') {
var data = mergeNodeDataIfNeeded(
parseAttributesAsData(attributesList),
graph.getNode(nodeAST.id)
);
if (data) {
graph.addNode(nodeAST.id, data);
} else {
graph.addNode(nodeAST.id);
}
return [nodeAST.id];
} else if (nodeAST.type === 'subgraph') {
return loadSubgraph(graph, nodeAST);
}
}
function addLink(graph, from, to, data) {
for (var i = 0; i < from.length; ++i) {
for (var j = 0; j < to.length; ++j) {
graph.addLink(from[i], to[j], data);
}
}
}
function parseAttributesAsData(attributesList) {
if (!attributesList || !attributesList.length) return;
var data = Object.create(null);
for (var i = 0; i < attributesList.length; ++i) {
var attr = attributesList[i];
if (attr.type !== 'attr' || attr.id === undefined) continue;
data[attr.id] = attr.eq;
}
return data;
}
function mergeNodeDataIfNeeded(newData, oldNode) {
if (!oldNode || !oldNode.data) return newData;
return Object.assign(oldNode.data, newData);
}