-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
138 lines (116 loc) · 3.49 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
129
130
131
132
133
134
135
136
137
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;
var maybeArray = attr.eq[0] === '[' && attr.eq[attr.eq.length - 1] === ']';
if (maybeArray) {
try {
var eq = JSON.parse(attr.eq);
attr.eq = eq;;
} catch (e) {
// ignore errors
}
}
data[attr.id] = attr.eq;
}
return data;
}
function mergeNodeDataIfNeeded(newData, oldNode) {
if (!oldNode || !oldNode.data) return newData;
return Object.assign(oldNode.data, newData);
}