-
Notifications
You must be signed in to change notification settings - Fork 8
/
handleOperators.js
226 lines (211 loc) · 7.22 KB
/
handleOperators.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
uuidv4 = require('uuidv4');
constants = require('./constants');
/**
* Receives object from parsed GOLDBAR specification and recursively builds a graph
* @param parsed The parsed GOLDBAR object
* @param stateGraph Current graph
* @param boundaryStack Boundary stack
* @param representation String: which representation of the graph
* @param categories Object: categories that the user input
* @param maxCycles int: maximum number of cycles
* @param andFlag boolean: whether there is an AND in the design
* @param mergeFlag boolean: whether there is a MERGE in the design
*/
function populateGraph(parsed, stateGraph, boundaryStack, representation, categories, maxCycles, andFlag, mergeFlag) {
// Handle if input is an atom
if (parsed.Atom) {
handleAtom(parsed.Atom, stateGraph, boundaryStack, categories, representation);
return;
}
if (parsed.ReverseComp) {
handleRevComp(parsed.ReverseComp, stateGraph, boundaryStack, categories, representation);
return;
}
// Handle if input is an or
if (Array.isArray(parsed)) {
for (let i = 0; i < parsed.length; i++) {
populateGraph(parsed[i], stateGraph, boundaryStack, representation, categories, maxCycles, andFlag, mergeFlag);
}
return;
}
// Handle if input contains other or nested operations
if (Object.keys(parsed).length > 0) {
for (let operation in parsed) {
let populateArgs = {
parsed: parsed[operation],
categories,
maxCycles,
andFlag,
mergeFlag
};
if (operation === constants.AND0) {
andFlag.pop();
andFlag.push(true);
handleOp(constants.AND, stateGraph, boundaryStack, representation, populateArgs, 0);
return;
} else if (operation === constants.AND1) {
andFlag.pop();
andFlag.push(true);
handleOp(constants.AND, stateGraph, boundaryStack, representation, populateArgs, 1);
return;
} else if (operation === constants.AND2) {
andFlag.pop();
andFlag.push(true);
handleOp(constants.AND, stateGraph, boundaryStack, representation, populateArgs, 2);
return;
} else if (operation === constants.OR && representation === constants.EDGE) {
populateArgs.parsed = flattenOp(parsed[operation], operation);
handleOp(operation, stateGraph, boundaryStack, representation, populateArgs);
} else if (operation === constants.MERGE) {
mergeFlag.pop();
mergeFlag.push(true);
handleOp(constants.MERGE, stateGraph, boundaryStack, representation, populateArgs);
return;
} else {
populateGraph(parsed[operation], stateGraph, boundaryStack, representation, categories, maxCycles, andFlag, mergeFlag);
handleOp(operation, stateGraph, boundaryStack, representation, populateArgs);
}
}
}
}
function flattenOp(parsedAtOp, operation) {
let objOfOp = [];
for (let obj of parsedAtOp) {
if (obj[operation]) {
objOfOp = objOfOp.concat(flattenOp(obj[operation], operation))
} else {
objOfOp.push(obj);
}
}
return objOfOp;
}
/**
* Calls appropriate graph functions not related to operators
* @param stateGraph Current graph
* @param boundaryStack Boundary stack
* @param atom Atom to add
* @param categories Object: categories that the user input
*/
function handleAtom(atom, stateGraph, boundaryStack, categories){
stateGraph.handleAtom(atom, boundaryStack, categories);
}
/**
* Calls appropriate graph functions not related to operators
* @param stateGraph Current graph
* @param boundaryStack Boundary stack
* @param atom Atom to add
* @param categories Object: categories that the user input
*/
function handleRevComp(atom, stateGraph, boundaryStack, categories){
stateGraph.handleRevComp(atom, boundaryStack, categories);
}
/**
* Calls appropriate graph functions related to operators
* @param op Operator or accept or root
* @param stateGraph Current graph
* @param boundaryStack Boundary stack
* @param representation NODE or EDGE
* @param populateArgs Object: arguments to populateGraph (from graphGOLDBAR)
* @param tolerance number: tolerance level for AND
*/
function handleOp(op, stateGraph, boundaryStack, representation, populateArgs, tolerance) {
switch (op) {
case constants.ONE:
return;
case constants.ACCEPT:
if (representation === constants.EDGE) {
stateGraph.addAcceptNodes(boundaryStack);
} else {
return stateGraph.addAcceptNodes(boundaryStack);
}
break;
case constants.ROOT:
return stateGraph.generateRootNode(boundaryStack);
case constants.OR:
if (representation === constants.EDGE) {
stateGraph.handleOr(boundaryStack, representation, populateArgs);
} else {
stateGraph.handleOr(boundaryStack);
}
break;
case constants.OR_SBOL:
if (representation === constants.EDGE) {
stateGraph.handleOrSBOL(boundaryStack);
} else {
stateGraph.handleOr(boundaryStack);
}
break;
case constants.AND:
if (representation === constants.EDGE) {
stateGraph.handleAnd(boundaryStack, representation, populateArgs, tolerance);
} else {
throw new Error('The AND operation is not supported in the NODE representation');
}
break;
case constants.MERGE:
if (representation === constants.EDGE) {
stateGraph.handleMerge(boundaryStack, representation, populateArgs);
} else {
throw new Error('The MERGE operation is not supported in the NODE representation');
}
break;
case constants.THEN:
stateGraph.handleThen(boundaryStack);
break;
case constants.ZERO_MORE:
stateGraph.handleZeroOrMore(boundaryStack);
break;
case constants.ZERO_ONE:
stateGraph.handleZeroOrOne(boundaryStack);
break;
case constants.ONE_MORE:
stateGraph.handleOneOrMore(boundaryStack);
break;
case constants.ZERO_SBOL: // only exists in NodeGraph
stateGraph.handleZeroOrMoreSbol(boundaryStack);
break;
case constants.ZERO_ONE_SBOL: // only exists in NodeGraph
stateGraph.handleZeroOrOneSbol(boundaryStack);
break;
}
}
/**
* Calls appropriate function for path enumeration
* @param root The root node
* @param stateGraph Current graph
* @param maxCycles number of times to repeat through an orMore
* @param representation NODE or EDGE
* @returns {object} Array of paths through graph
*/
function enumerate(root, stateGraph, maxCycles, representation){
return stateGraph.enumeratePaths(root, maxCycles);
}
/**
* Minimizes a graph without enumerating paths
* @param stateGraph
* @param representation
*/
function collapse(stateGraph, representation) {
switch (representation) {
case constants.NODE:
let epsilonMap = stateGraph.getEpsilonParents();
return stateGraph.collapseEpsilons(epsilonMap);
case constants.EDGE:
// collapse a copy and save the original for SBOL generation
let collapsed = stateGraph.deepCopy();
let old = collapsed.deepCopy();
collapsed.collapseEpsilons();
while (!collapsed.equals(old)) {
old = collapsed.deepCopy();
collapsed.collapseEpsilons();
}
return collapsed;
}
}
module.exports = {
handleAtom,
handleOp,
collapse,
enumerate,
populateGraph
};