UNPKG

14.7 kBJavaScriptView Raw
1var grlib = require('graphlib')
2// var graphtools = require('@buggyorg/graphtools')
3import _ from 'lodash'
4import * as graphtools from '@buggyorg/graphtools'
5
6function isInPort (node) {
7 return (node['nodeType'] === 'inPort' && node['isTrans'] === undefined && !node['isTrans'])
8}
9
10function isOutPort (node) {
11 return (node['nodeType'] === 'outPort' && node['isTrans'] === undefined && !node['isTrans'])
12}
13
14function cloneGraph (graph) {
15 return grlib.json.read(grlib.json.write(graph))
16}
17
18function processOfEdge (edgeName) {
19 return edgeName.split('_').slice(0, -2).join('_')
20}
21
22function portOfEdge (graph, nodeName) {
23 // return edgeName.split('_')[3]
24 return graph.node(nodeName)['portName']
25}
26
27function getOutputs (graph, nodeId) {
28 return graph.node(nodeId)['outputPorts']
29}
30
31function getInputs (graph, nodeId) {
32 return graph.node(nodeId)['inputPorts']
33}
34
35var replaceAll = (str, search, replacement) => {
36 var repl = replacement
37 return str.split(search).join(repl)
38}
39
40function isGeneric (name) {
41 return name === 'generic' || name === '[generic]'
42}
43
44export function replaceGeneric (what, replacement) {
45 if (what === 'function' && typeof (replacement) === 'object') {
46 return replacement
47 } else if (what !== 'function' && typeof (replacement) === 'string') {
48 return replaceAll(what, 'generic', replacement)
49 } else {
50 return what
51 }
52}
53
54function entangleType (type, template) {
55 if (template[0] === '[' && template[template.length - 1] === ']') {
56 return type.replace(/\[/g, '').replace(/\]/g, '')
57 } else {
58 return type
59 }
60}
61
62function tangleType (type, template) {
63 if (template[0] === '[' && template[template.length - 1] === ']') {
64 return '[' + type + ']'
65 } else {
66 return type
67 }
68}
69
70function genericInputs (graph, node) {
71 var genericInputs = []
72 if (graph.node(node)['inputPorts'] !== undefined) {
73 var inputPorts = graph.node(node)['inputPorts']
74 var inputNames = Object.keys(inputPorts)
75 for (var i = 0; i < inputNames.length; i++) {
76 if (isGeneric(inputPorts[inputNames[i]])) {
77 genericInputs = genericInputs.concat([inputNames[i]])
78 }
79 }
80 }
81 return genericInputs
82}
83
84function genericOutputs (graph, node) {
85 var genericOutputs = []
86 if (graph.node(node)['outputPorts'] !== undefined) {
87 var outputPorts = graph.node(node)['outputPorts']
88 var outputNames = Object.keys(outputPorts)
89 for (var i = 0; i < outputNames.length; i++) {
90 if (isGeneric(outputPorts[outputNames[i]])) {
91 genericOutputs = genericOutputs.concat([outputNames[i]])
92 }
93 }
94 }
95 return genericOutputs
96}
97
98function genericInputPorts (graph, node, port) {
99 var curNode = graph.node(node)
100 if (curNode.inputPorts[port]) {
101 return _.filter(genericInputs(graph, node), (follow) => follow === port)
102 } else if (!curNode.atomic && !curNode.recursive) {
103 return _.filter(genericOutputs(graph, node), (follow) => follow === port)
104 } else {
105 return genericInputs(graph, node)
106 }
107}
108
109function genericOutputPorts (graph, node, port) {
110 var curNode = graph.node(node)
111 if (curNode.outputPorts[port]) {
112 return _.filter(genericOutputs(graph, node), (follow) => follow === port)
113 } else {
114 return _.filter(genericInputs(graph, node), (follow) => follow === port)
115 }
116}
117
118function followGenerics (graph, node, port) {
119 var curNode = graph.node(node)
120 if (port && !isGeneric(curNode.inputPorts[port]) && !isGeneric(curNode.outputPorts[port])) {
121 return []
122 } else {
123 return genericInputPorts(graph, node, port)
124 }
125}
126
127function followGenericsForward (graph, node, port) {
128 var curNode = graph.node(node)
129 if (port && !isGeneric(curNode.inputPorts[port]) && !isGeneric(curNode.outputPorts[port])) {
130 return []
131 } else {
132 return genericOutputPorts(graph, node, port)
133 }
134}
135
136function replaceTypeHints (graph) {
137 var editGraph = graphtools.utils.edit(graph)
138 _.merge(editGraph, {nodes: _.map(editGraph.nodes, (n) => {
139 if (!n.value.id) { return n }
140 return _.merge({}, n, { value: {
141 inputPorts: _.mapValues(n.value.inputPorts, (p, k) => (n.value.typeHint && n.value.typeHint[k]) ? n.value.typeHint[k] : p),
142 outputPorts: _.mapValues(n.value.outputPorts, (p, k) => (n.value.typeHint && n.value.typeHint[k]) ? n.value.typeHint[k] : p)
143 }})
144 })})
145 return graphtools.utils.finalize(editGraph)
146}
147
148/**
149 * replaces a generic input port by walking the path back until a non generic is found
150 *
151 * @param graph the graphlib graph
152 * @param node the node from which the generic port should be replaced
153 * @param node the port of the input node to follow
154 */
155export function replaceGenericInput (graph, node) {
156 var curNode = graph.node(node)
157 var ports
158 if (curNode.atomic) {
159 ports = curNode.inputPorts
160 } else {
161 ports = _.merge({}, curNode.inputPorts, curNode.outputPorts)
162 }
163 return _.filter(_.flatten(_.map(ports, (type, port) =>
164 _.map(graphtools.walk.walkBack(graph, {node, port}, followGenerics, {keepPorts: true}),
165 (path) =>
166 _.reject(path, (n) => n.port === null))))
167 , (list) => list.length > 1)
168}
169
170export function replaceGenericOutput (graph, node) {
171 var curNode = graph.node(node)
172 var ports
173 if (curNode.atomic) {
174 ports = curNode.outputPorts
175 } else {
176 ports = _.merge({}, curNode.inputPorts, curNode.outputPorts)
177 }
178 return _.filter(_.flatten(_.map(ports, (type, port) =>
179 graphtools.walk.walk(graph, {node, port}, followGenericsForward, {keepPorts: true})))
180 , (list) => list.length > 1)
181}
182
183export function replaceGenerics (graph) {
184 var processGraph = replaceTypeHints(graph)
185 var nodes = processGraph.nodes()
186 for (var j = 0; j < nodes.length; j++) {
187 var paths = replaceGenericInput(graph, nodes[j])
188 // var types = ''
189 var pathsToReplace = []
190 for (let i = 0; i < paths.length; i++) {
191 var currentPath = paths[i]
192 var type = processGraph.node(currentPath[0].node).outputPorts[currentPath[0].port] ||
193 processGraph.node(currentPath[0].node).inputPorts[currentPath[0].port]
194 /* if (types === '') {
195 types = type
196 } else if (type !== types) {
197 var error = 'Type mismatch: Two pathes ending in node ' + currentPath[currentPath.length - 1].node + ' have different types: ' + types + ' and ' + type
198 throw new Error(error)
199 }*/
200 if (type === 'generic') {
201 pathsToReplace = pathsToReplace.concat([currentPath])
202 } else {
203 var validType = type
204 replacePathGenerics(processGraph, currentPath, type)
205 }
206 }
207 if (validType === undefined && pathsToReplace.length !== 0) {
208 var secondInputs = processGraph.node(nodes[j]).inputPorts
209 var keys = Object.keys(secondInputs)
210 for (let i = 0; i < keys.length; i++) {
211 if (secondInputs[keys[i]] !== 'generic') {
212 validType = secondInputs[keys[i]]
213 }
214 }
215 }
216 if (validType === undefined && pathsToReplace.length !== 0) {
217 paths = replaceGenericOutput(graph, nodes[j])
218 for (let i = 0; i < paths.length; i++) {
219 currentPath = _.filter(paths[i], (node) => node.port !== null)
220 type = processGraph.node(currentPath[currentPath.length - 1].node).inputPorts[currentPath[currentPath.length - 1].port] ||
221 processGraph.node(currentPath[currentPath.length - 1].node).outputPorts[currentPath[currentPath.length - 1].port]
222 if (type === 'generic') {
223 pathsToReplace = pathsToReplace.concat([currentPath])
224 } else {
225 validType = type
226 replacePathGenerics(processGraph, currentPath, type)
227 }
228 }
229 }
230 if (validType === undefined && pathsToReplace.length !== 0) {
231 throw new Error('Generics could not be replaced: No type found.')
232 }
233 for (let p = 0; p < pathsToReplace.length; p++) {
234 replacePathGenerics(processGraph, pathsToReplace[p], validType, pathsToReplace[p][0].port)
235 }
236 }
237 return processGraph
238}
239
240function replacePathGenerics (graph, path, type, firstPort) {
241 for (var r = 0; r < path.length - 1; r++) {
242 var fromNode = graph.node(path[r].edge.from)
243 var fromType = (graph.parent(path[r].edge.to) === path[r].edge.from) ? fromNode.inputPorts[path[r].edge.outPort] : fromNode.outputPorts[path[r].edge.outPort]
244 // if the output of this node is not yet set
245 if (isGeneric(fromType)) {
246 // there should be some other path that should have assigned the generic type
247 if (!fromNode.generic || !fromNode.genericType) {
248 throw new Error('Cannot resolve generic type for ' + path[r].edge.from + ' on path ' + JSON.stringify(path))
249 }
250 fromType = tangleType(fromNode.genericType, fromType)
251 fromNode.outputPorts[path[r].edge.outPort] = fromType
252 }
253 var toNode = graph.node(path[r].edge.to)
254 var toType = (graph.parent(path[r].edge.from) === path[r].edge.to) ? toNode.outputPorts[path[r].edge.inPort] : toNode.inputPorts[path[r].edge.inPort]
255 var entangled = toNode.genericType || entangleType(fromType, toType)
256 toNode.generic = true
257 toNode.genericType = entangled
258 if (isGeneric(toType)) {
259 toNode.inputPorts[path[r].edge.inPort] = tangleType(entangled, toType)
260 }
261 }
262 var lastNode = path[path.length - 1].node
263 var genOutput = genericOutputs(graph, lastNode)
264 for (var l = 0; l < genOutput.length; l++) {
265 graph.node(lastNode).outputPorts[genOutput[l]] =
266 tangleType(graph.node(lastNode).genericType, graph.node(lastNode).outputPorts[genOutput[l]])
267 }
268 /* for (var k = 1; k < path.length; k++) {
269 var curNode = graph.node(path[k].node)
270 var curPort = curNode.inputPorts[path[k].port]
271 if (!curPort) {
272 if (curNode.outputPorts[path[k].port] && curNode.genericType) {
273 curType = tangleType(curNode.genericType, curNode.outputPorts[path[k].port])
274 }
275 continue
276 }
277 curNode.genericType = entangleType(curType, curPort)
278 curNode.generic = true
279 curType = curNode.genericType
280 var genInput = genericInputs(graph, path[k].node)
281 for (var l = 0; l < genInput.length; l++) {
282 curNode.inputPorts[genInput[l]] =
283 replaceGeneric(curNode.inputPorts[genInput[l]], curNode.genericType)
284 }
285 var genOutput = genericOutputs(graph, path[k].node)
286 for (var m = 0; m < genOutput.length; m++) {
287 curNode.outputPorts[genOutput[m]] =
288 replaceGeneric(curNode.outputPorts[genOutput[m]], curNode.genericType)
289 }
290 }
291 if (firstPort !== undefined) {
292 graph.node(path[0].node).generic = true
293 graph.node(path[0].node).outputPorts[firstPort] =
294 replaceGeneric(graph.node(path[0].node).outputPorts[firstPort], type)
295 }*/
296}
297
298export function addTypeConversion (processGraph, convertGraph) {
299 var newProcessGraph = cloneGraph(processGraph)
300 // Add Translator nodes
301 for (let edge of newProcessGraph.edges()) {
302 // Translator nodes only exist between Ports
303 var v = newProcessGraph.node(edge.v)
304 var w = newProcessGraph.node(edge.w)
305 if (isOutPort(v) && isInPort(w)) {
306 var labelIn = edge.v
307 var labelOut = edge.w
308 // get datatypes
309 var processV = processOfEdge(labelIn)
310 var processW = processOfEdge(labelOut)
311 var portNameV = portOfEdge(newProcessGraph, labelIn)
312 var portNameW = portOfEdge(newProcessGraph, labelOut)
313 var typeV = getOutputs(newProcessGraph, processV)[portNameV]
314 var typeW = getInputs(newProcessGraph, processW)[portNameW]
315
316 // if the types are different add translator
317 if (typeV !== typeW) {
318 newProcessGraph.removeEdge(labelIn, labelOut)
319 var parentV = processGraph.parent(labelIn)
320 // datatype translator
321 var dijkstra = grlib.alg.dijkstra(convertGraph, typeV)
322 var way = [typeW]
323 while (way[way.length - 1] !== typeV) {
324 way.push(dijkstra[way[way.length - 1]].predecessor)
325 }
326 for (var k = way.length - 1; k >= 1; k--) {
327 var number = way.length - k
328 var id = labelIn + ':' + labelOut + '_' + number
329 // translator nodes
330 // newProcessGraph.setNode(id, {'nodeType': 'process', 'typeFrom': way[k], 'typeTo': way[k - 1]})
331 var meta = 'translator/' + way[k] + '_to_' + way[k - 1]
332 newProcessGraph.setNode(id, {'nodeType': 'process', 'id': meta, 'atomic': 'true'})
333 newProcessGraph.setNode(id + '_PORT_in', {'nodeType': 'inPort', 'portName': 'input', 'isTrans': true})
334 newProcessGraph.setNode(id + '_PORT_out', {'nodeType': 'outPort', 'portName': 'output', 'isTrans': true})
335 // translator edges
336 newProcessGraph.setEdge(id + '_PORT_in', id)
337 newProcessGraph.setEdge(id, id + '_PORT_out')
338 // set parents TODO: parent ok?
339 newProcessGraph.setParent(id, parentV)
340 newProcessGraph.setParent(id + '_PORT_in', parentV)
341 newProcessGraph.setParent(id + '_PORT_out', parentV)
342 // edges between translators and between processes and translators
343 // is first translator
344 if (k === way.length - 1) {
345 newProcessGraph.setEdge(labelIn, id + '_PORT_in')
346 } else {
347 newProcessGraph.setEdge(labelIn + ':' + labelOut + '_' + (number - 1) + '_PORT_out', id + '_PORT_in')
348 }
349 // is last translator
350 if (k === 1) {
351 newProcessGraph.setEdge(id + '_PORT_out', labelOut)
352 }
353 }
354 }
355 }
356 }
357 return newProcessGraph
358}
359
360export function genericNodes (graph) {
361 var nodes = graph.nodes()
362 var genNodes = []
363 for (let i = 0; i < nodes.length; i++) {
364 var inp = graph.node(nodes[i]).inputPorts
365 if (inp !== undefined) {
366 var inpKeys = Object.keys(inp)
367 for (let j = 0; j < inpKeys.length; j++) {
368 if (isGeneric(inp[inpKeys[j]])) {
369 genNodes.push(nodes[i])
370 }
371 }
372 }
373 var outp = graph.node(nodes[i]).outputPorts
374 if (outp !== undefined) {
375 var outpKeys = Object.keys(outp)
376 for (let j = 0; j < outpKeys.length; j++) {
377 if (isGeneric(outp[outpKeys[j]])) {
378 genNodes.push(nodes[i])
379 }
380 }
381 }
382 }
383 return genNodes
384}
385
386export function isGenericFree (graph) {
387 var nodes = graph.nodes()
388 for (let i = 0; i < nodes.length; i++) {
389 var inp = graph.node(nodes[i]).inputPorts
390 if (inp !== undefined) {
391 var inpKeys = Object.keys(inp)
392 for (let j = 0; j < inpKeys.length; j++) {
393 if (isGeneric(inp[inpKeys[j]])) {
394 return false
395 }
396 }
397 }
398 var outp = graph.node(nodes[i]).outputPorts
399 if (outp !== undefined) {
400 var outpKeys = Object.keys(outp)
401 for (let j = 0; j < outpKeys.length; j++) {
402 if (isGeneric(outp[outpKeys[j]])) {
403 return false
404 }
405 }
406 }
407 }
408 return true
409}