ts2workflows 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +36 -9
- package/dist/ast/expressions.d.ts +37 -41
- package/dist/ast/expressions.d.ts.map +1 -1
- package/dist/ast/expressions.js +124 -255
- package/dist/ast/statements.d.ts +132 -0
- package/dist/ast/statements.d.ts.map +1 -0
- package/dist/ast/statements.js +197 -0
- package/dist/ast/steps.d.ts +82 -194
- package/dist/ast/steps.d.ts.map +1 -1
- package/dist/ast/steps.js +862 -521
- package/dist/ast/workflows.d.ts +14 -12
- package/dist/ast/workflows.d.ts.map +1 -1
- package/dist/ast/workflows.js +20 -35
- package/dist/cli.js +45 -32
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/transpiler/index.d.ts +19 -1
- package/dist/transpiler/index.d.ts.map +1 -1
- package/dist/transpiler/index.js +158 -31
- package/dist/transpiler/linker.d.ts +3 -0
- package/dist/transpiler/linker.d.ts.map +1 -0
- package/dist/transpiler/linker.js +41 -0
- package/dist/transpiler/parseexpressions.d.ts +11 -0
- package/dist/transpiler/parseexpressions.d.ts.map +1 -0
- package/dist/transpiler/{expressions.js → parseexpressions.js} +90 -103
- package/dist/transpiler/parsestatement.d.ts +7 -0
- package/dist/transpiler/parsestatement.d.ts.map +1 -0
- package/dist/transpiler/parsestatement.js +862 -0
- package/dist/transpiler/stepnames.d.ts +3 -0
- package/dist/transpiler/stepnames.d.ts.map +1 -0
- package/dist/transpiler/stepnames.js +17 -0
- package/dist/transpiler/transformations.d.ts +3 -19
- package/dist/transpiler/transformations.d.ts.map +1 -1
- package/dist/transpiler/transformations.js +267 -322
- package/language_reference.md +53 -17
- package/package.json +15 -11
- package/types/workflowslib.d.ts +58 -74
- package/dist/ast/stepnames.d.ts +0 -9
- package/dist/ast/stepnames.d.ts.map +0 -1
- package/dist/ast/stepnames.js +0 -280
- package/dist/ast/validation.d.ts +0 -20
- package/dist/ast/validation.d.ts.map +0 -1
- package/dist/ast/validation.js +0 -214
- package/dist/transpiler/expressions.d.ts +0 -11
- package/dist/transpiler/expressions.d.ts.map +0 -1
- package/dist/transpiler/statements.d.ts +0 -10
- package/dist/transpiler/statements.d.ts.map +0 -1
- package/dist/transpiler/statements.js +0 -1100
- package/dist/utils.d.ts +0 -9
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -13
package/dist/ast/steps.js
CHANGED
|
@@ -1,538 +1,48 @@
|
|
|
1
1
|
import * as R from 'ramda';
|
|
2
|
-
import {
|
|
3
|
-
import { expressionToLiteralValueOrLiteralExpression, } from './expressions.js';
|
|
2
|
+
import { InternalTranspilingError } from '../errors.js';
|
|
3
|
+
import { binaryEx, expressionToLiteralValueOrLiteralExpression, expressionToString, nullEx, stringEx, variableReferenceEx, } from './expressions.js';
|
|
4
4
|
import { Subworkflow } from './workflows.js';
|
|
5
|
-
export class SubworkflowAST {
|
|
6
|
-
name;
|
|
7
|
-
steps;
|
|
8
|
-
params;
|
|
9
|
-
constructor(name, steps, params) {
|
|
10
|
-
this.name = name;
|
|
11
|
-
this.steps = steps;
|
|
12
|
-
this.params = params;
|
|
13
|
-
}
|
|
14
|
-
withStepNames(generate) {
|
|
15
|
-
const steps = this.steps.map((step) => namedSteps(step, generate));
|
|
16
|
-
return new Subworkflow(this.name, steps, this.params);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step
|
|
20
|
-
export class AssignStepAST {
|
|
21
|
-
tag = 'assign';
|
|
22
|
-
assignments;
|
|
23
|
-
label;
|
|
24
|
-
next;
|
|
25
|
-
constructor(assignments, next, label) {
|
|
26
|
-
this.assignments = assignments;
|
|
27
|
-
this.next = next;
|
|
28
|
-
this.label = label;
|
|
29
|
-
}
|
|
30
|
-
withNext(newNext) {
|
|
31
|
-
if (newNext === this.next) {
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
return new AssignStepAST(this.assignments, newNext, this.label);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
withLabel(newLabel) {
|
|
39
|
-
return new AssignStepAST(this.assignments, this.next, newLabel);
|
|
40
|
-
}
|
|
41
|
-
applyNestedSteps() {
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/calls
|
|
46
|
-
export class CallStepAST {
|
|
47
|
-
tag = 'call';
|
|
48
|
-
call;
|
|
49
|
-
args;
|
|
50
|
-
result;
|
|
51
|
-
label;
|
|
52
|
-
constructor(call, args, result, label) {
|
|
53
|
-
this.call = call;
|
|
54
|
-
this.args = args;
|
|
55
|
-
this.result = result;
|
|
56
|
-
this.label = label;
|
|
57
|
-
}
|
|
58
|
-
labelPrefix() {
|
|
59
|
-
return 'call_' + this.call.replaceAll(/[^a-zA-Z0-9]/g, '_') + '_';
|
|
60
|
-
}
|
|
61
|
-
withLabel(newLabel) {
|
|
62
|
-
return new CallStepAST(this.call, this.args, this.result, newLabel);
|
|
63
|
-
}
|
|
64
|
-
applyNestedSteps() {
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/iteration
|
|
69
|
-
export class ForStepAST {
|
|
70
|
-
tag = 'for';
|
|
71
|
-
steps;
|
|
72
|
-
loopVariableName;
|
|
73
|
-
indexVariableName;
|
|
74
|
-
listExpression;
|
|
75
|
-
label;
|
|
76
|
-
constructor(steps, loopVariableName, listExpression, indexVariable, label) {
|
|
77
|
-
this.steps = steps;
|
|
78
|
-
this.loopVariableName = loopVariableName;
|
|
79
|
-
this.listExpression = listExpression;
|
|
80
|
-
this.indexVariableName = indexVariable;
|
|
81
|
-
this.label = label;
|
|
82
|
-
}
|
|
83
|
-
withLabel(newLabel) {
|
|
84
|
-
return new ForStepAST(this.steps, this.loopVariableName, this.listExpression, this.indexVariableName, newLabel);
|
|
85
|
-
}
|
|
86
|
-
applyNestedSteps(fn) {
|
|
87
|
-
return new ForStepAST(fn(this.steps), this.loopVariableName, this.listExpression, this.indexVariableName, this.label);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
export class ForRangeStepAST {
|
|
91
|
-
tag = 'forrange';
|
|
92
|
-
steps;
|
|
93
|
-
loopVariableName;
|
|
94
|
-
rangeStart;
|
|
95
|
-
rangeEnd;
|
|
96
|
-
label;
|
|
97
|
-
constructor(steps, loopVariableName, rangeStart, rangeEnd, label) {
|
|
98
|
-
this.steps = steps;
|
|
99
|
-
this.loopVariableName = loopVariableName;
|
|
100
|
-
this.rangeStart = rangeStart;
|
|
101
|
-
this.rangeEnd = rangeEnd;
|
|
102
|
-
this.label = label;
|
|
103
|
-
}
|
|
104
|
-
withLabel(newLabel) {
|
|
105
|
-
return new ForRangeStepAST(this.steps, this.loopVariableName, this.rangeStart, this.rangeEnd, newLabel);
|
|
106
|
-
}
|
|
107
|
-
applyNestedSteps(fn) {
|
|
108
|
-
return new ForRangeStepAST(fn(this.steps), this.loopVariableName, this.rangeStart, this.rangeEnd, this.label);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
export class ForStepASTNamed {
|
|
112
|
-
tag = 'for';
|
|
113
|
-
steps;
|
|
114
|
-
loopVariableName;
|
|
115
|
-
indexVariableName;
|
|
116
|
-
listExpression;
|
|
117
|
-
rangeStart;
|
|
118
|
-
rangeEnd;
|
|
119
|
-
constructor(steps, loopVariableName, listExpression, indexVariable, rangeStart, rangeEnd) {
|
|
120
|
-
this.steps = steps;
|
|
121
|
-
this.loopVariableName = loopVariableName;
|
|
122
|
-
this.listExpression = listExpression;
|
|
123
|
-
this.indexVariableName = indexVariable;
|
|
124
|
-
this.rangeStart = rangeStart;
|
|
125
|
-
this.rangeEnd = rangeEnd;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
export class NextStepAST {
|
|
129
|
-
tag = 'next';
|
|
130
|
-
target;
|
|
131
|
-
label;
|
|
132
|
-
constructor(target, label) {
|
|
133
|
-
this.target = target;
|
|
134
|
-
this.label = label;
|
|
135
|
-
}
|
|
136
|
-
withLabel(newLabel) {
|
|
137
|
-
return new NextStepAST(this.target, newLabel);
|
|
138
|
-
}
|
|
139
|
-
applyNestedSteps() {
|
|
140
|
-
return this;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/parallel-steps
|
|
144
|
-
export class ParallelStepAST {
|
|
145
|
-
tag = 'parallel';
|
|
146
|
-
steps;
|
|
147
|
-
shared;
|
|
148
|
-
concurrencyLimit;
|
|
149
|
-
exceptionPolicy;
|
|
150
|
-
label;
|
|
151
|
-
constructor(steps, shared, concurrencyLimit, exceptionPolicy, label) {
|
|
152
|
-
this.steps = steps;
|
|
153
|
-
this.shared = shared;
|
|
154
|
-
this.concurrencyLimit = concurrencyLimit;
|
|
155
|
-
this.exceptionPolicy = exceptionPolicy;
|
|
156
|
-
this.label = label;
|
|
157
|
-
}
|
|
158
|
-
withLabel(newLabel) {
|
|
159
|
-
return new ParallelStepAST(this.steps, this.shared, this.concurrencyLimit, this.exceptionPolicy, newLabel);
|
|
160
|
-
}
|
|
161
|
-
applyNestedSteps(fn) {
|
|
162
|
-
let transformedSteps;
|
|
163
|
-
if (this.steps instanceof ForStepAST) {
|
|
164
|
-
transformedSteps = this.steps.applyNestedSteps(fn);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
transformedSteps = R.map((s) => new StepsStepAST(fn(s.steps), s.label), this.steps);
|
|
168
|
-
}
|
|
169
|
-
return new ParallelStepAST(transformedSteps, this.shared, this.concurrencyLimit, this.exceptionPolicy, this.label);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
export class ParallelStepASTNamed {
|
|
173
|
-
tag = 'parallel';
|
|
174
|
-
branches; // Either steps for each branch
|
|
175
|
-
forStep; // ... or a parallel for
|
|
176
|
-
shared;
|
|
177
|
-
concurrenceLimit;
|
|
178
|
-
exceptionPolicy;
|
|
179
|
-
constructor(steps, shared, concurrencyLimit, exceptionPolicy) {
|
|
180
|
-
this.shared = shared;
|
|
181
|
-
this.concurrenceLimit = concurrencyLimit;
|
|
182
|
-
this.exceptionPolicy = exceptionPolicy;
|
|
183
|
-
if (!isRecord(steps)) {
|
|
184
|
-
this.forStep = steps;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
this.branches = Object.entries(steps).map((x) => {
|
|
188
|
-
return { name: x[0], step: x[1] };
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/raising-errors
|
|
194
|
-
export class RaiseStepAST {
|
|
195
|
-
tag = 'raise';
|
|
196
|
-
value;
|
|
197
|
-
label;
|
|
198
|
-
constructor(value, label) {
|
|
199
|
-
this.value = value;
|
|
200
|
-
this.label = label;
|
|
201
|
-
}
|
|
202
|
-
withLabel(newLabel) {
|
|
203
|
-
return new RaiseStepAST(this.value, newLabel);
|
|
204
|
-
}
|
|
205
|
-
applyNestedSteps() {
|
|
206
|
-
return this;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/completing
|
|
210
|
-
export class ReturnStepAST {
|
|
211
|
-
tag = 'return';
|
|
212
|
-
value;
|
|
213
|
-
label;
|
|
214
|
-
constructor(value, label) {
|
|
215
|
-
this.value = value;
|
|
216
|
-
this.label = label;
|
|
217
|
-
}
|
|
218
|
-
withLabel(newLabel) {
|
|
219
|
-
return new ReturnStepAST(this.value, newLabel);
|
|
220
|
-
}
|
|
221
|
-
applyNestedSteps() {
|
|
222
|
-
return this;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/steps#embedded-steps
|
|
226
|
-
export class StepsStepAST {
|
|
227
|
-
tag = 'steps';
|
|
228
|
-
steps;
|
|
229
|
-
label;
|
|
230
|
-
constructor(steps, label) {
|
|
231
|
-
this.steps = steps;
|
|
232
|
-
this.label = label;
|
|
233
|
-
}
|
|
234
|
-
withLabel(newLabel) {
|
|
235
|
-
return new StepsStepAST(this.steps, newLabel);
|
|
236
|
-
}
|
|
237
|
-
applyNestedSteps(fn) {
|
|
238
|
-
return new StepsStepAST(fn(this.steps), this.label);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
export class StepsStepASTNamed {
|
|
242
|
-
tag = 'steps';
|
|
243
|
-
steps;
|
|
244
|
-
constructor(steps) {
|
|
245
|
-
this.steps = steps;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/conditions
|
|
249
|
-
export class SwitchStepAST {
|
|
250
|
-
tag = 'switch';
|
|
251
|
-
branches;
|
|
252
|
-
label;
|
|
253
|
-
constructor(branches, label) {
|
|
254
|
-
this.branches = branches;
|
|
255
|
-
this.label = label;
|
|
256
|
-
}
|
|
257
|
-
withLabel(newLabel) {
|
|
258
|
-
return new SwitchStepAST(this.branches, newLabel);
|
|
259
|
-
}
|
|
260
|
-
applyNestedSteps(fn) {
|
|
261
|
-
return new SwitchStepAST(this.branches.map((branch) => ({
|
|
262
|
-
condition: branch.condition,
|
|
263
|
-
steps: fn(branch.steps),
|
|
264
|
-
next: branch.next,
|
|
265
|
-
})), this.label);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
export class SwitchStepASTNamed {
|
|
269
|
-
tag = 'switch';
|
|
270
|
-
conditions;
|
|
271
|
-
next;
|
|
272
|
-
constructor(conditions, next) {
|
|
273
|
-
this.conditions = conditions;
|
|
274
|
-
this.next = next;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// https://cloud.google.com/workflows/docs/reference/syntax/catching-errors
|
|
278
|
-
export class TryStepAST {
|
|
279
|
-
tag = 'try';
|
|
280
|
-
trySteps;
|
|
281
|
-
exceptSteps;
|
|
282
|
-
retryPolicy;
|
|
283
|
-
errorMap;
|
|
284
|
-
label;
|
|
285
|
-
constructor(trySteps, exceptSteps, retryPolicy, errorMap, label) {
|
|
286
|
-
this.trySteps = trySteps;
|
|
287
|
-
this.exceptSteps = exceptSteps;
|
|
288
|
-
this.retryPolicy = retryPolicy;
|
|
289
|
-
this.errorMap = errorMap;
|
|
290
|
-
this.label = label;
|
|
291
|
-
}
|
|
292
|
-
withLabel(newLabel) {
|
|
293
|
-
return new TryStepAST(this.trySteps, this.exceptSteps, this.retryPolicy, this.errorMap, newLabel);
|
|
294
|
-
}
|
|
295
|
-
applyNestedSteps(fn) {
|
|
296
|
-
return new TryStepAST(fn(this.trySteps), this.exceptSteps ? fn(this.exceptSteps) : undefined, this.retryPolicy, this.errorMap, this.label);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
export class TryStepASTNamed {
|
|
300
|
-
tag = 'try';
|
|
301
|
-
retryPolicy;
|
|
302
|
-
errorMap;
|
|
303
|
-
// Steps in the try block
|
|
304
|
-
trySteps;
|
|
305
|
-
// Steps in the except block
|
|
306
|
-
exceptSteps;
|
|
307
|
-
constructor(steps, exceptSteps, retryPolicy, errorMap) {
|
|
308
|
-
this.trySteps = steps;
|
|
309
|
-
this.retryPolicy = retryPolicy;
|
|
310
|
-
this.errorMap = errorMap;
|
|
311
|
-
this.exceptSteps = exceptSteps;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
// Internal step that represents a potential jump target.
|
|
315
|
-
// This can be ued as a placeholder when the actual target step is not yet known.
|
|
316
|
-
// JumpTargetAST is removed before transpiling to workflows YAML.
|
|
317
|
-
export class JumpTargetAST {
|
|
318
|
-
tag = 'jumptarget';
|
|
319
|
-
label;
|
|
320
|
-
constructor() {
|
|
321
|
-
this.label = `jumptarget_${Math.floor(Math.random() * 2 ** 32).toString(16)}`;
|
|
322
|
-
}
|
|
323
|
-
applyNestedSteps() {
|
|
324
|
-
return this;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Assign a name for this step and its child steps.
|
|
329
|
-
*
|
|
330
|
-
* The name is generated by calling generateName with a prefix identifying the step type.
|
|
331
|
-
*/
|
|
332
|
-
export function namedSteps(step, generateName) {
|
|
333
|
-
switch (step.tag) {
|
|
334
|
-
case 'assign':
|
|
335
|
-
case 'next':
|
|
336
|
-
case 'raise':
|
|
337
|
-
case 'return':
|
|
338
|
-
return {
|
|
339
|
-
name: step.label ?? generateName(step.tag),
|
|
340
|
-
step,
|
|
341
|
-
};
|
|
342
|
-
case 'call':
|
|
343
|
-
return {
|
|
344
|
-
name: step.label ?? generateName(step.labelPrefix()),
|
|
345
|
-
step,
|
|
346
|
-
};
|
|
347
|
-
case 'for':
|
|
348
|
-
return namedStepsFor(step, generateName);
|
|
349
|
-
case 'forrange':
|
|
350
|
-
return namedStepsForRange(step, generateName);
|
|
351
|
-
case 'parallel':
|
|
352
|
-
return namedStepsParallel(step, generateName);
|
|
353
|
-
case 'steps':
|
|
354
|
-
return namedStepsSteps(step, generateName);
|
|
355
|
-
case 'switch':
|
|
356
|
-
return namedStepsSwitch(step, generateName);
|
|
357
|
-
case 'try':
|
|
358
|
-
return namedStepsTry(step, generateName);
|
|
359
|
-
case 'jumptarget':
|
|
360
|
-
return {
|
|
361
|
-
name: step.label,
|
|
362
|
-
step: step,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
function namedStepsFor(step, generateName) {
|
|
367
|
-
return {
|
|
368
|
-
name: step.label ?? generateName('for'),
|
|
369
|
-
step: new ForStepASTNamed(step.steps.map((nestedStep) => namedSteps(nestedStep, generateName)), step.loopVariableName, step.listExpression, step.indexVariableName),
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
function namedStepsForRange(step, generateName) {
|
|
373
|
-
return {
|
|
374
|
-
name: step.label ?? generateName('for'),
|
|
375
|
-
step: new ForStepASTNamed(step.steps.map((nestedStep) => namedSteps(nestedStep, generateName)), step.loopVariableName, undefined, undefined, step.rangeStart, step.rangeEnd),
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
function namedStepsParallel(step, generateName) {
|
|
379
|
-
let steps;
|
|
380
|
-
const mainLabel = step.label ?? generateName('parallel');
|
|
381
|
-
if (!isRecord(step.steps)) {
|
|
382
|
-
const forStep = namedSteps(step.steps, generateName).step;
|
|
383
|
-
if (forStep.tag !== 'for') {
|
|
384
|
-
throw new Error(`Encountered a step of type ${forStep.tag} when a for step was expected`);
|
|
385
|
-
}
|
|
386
|
-
steps = forStep;
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
steps = R.map((step) => {
|
|
390
|
-
const named = step.steps.map((x) => namedSteps(x, generateName));
|
|
391
|
-
return new StepsStepASTNamed(named);
|
|
392
|
-
}, step.steps);
|
|
393
|
-
}
|
|
394
|
-
return {
|
|
395
|
-
name: mainLabel,
|
|
396
|
-
step: new ParallelStepASTNamed(steps, step.shared, step.concurrencyLimit, step.exceptionPolicy),
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
function namedStepsSteps(step, generateName) {
|
|
400
|
-
return {
|
|
401
|
-
name: step.label ?? generateName('steps'),
|
|
402
|
-
step: new StepsStepASTNamed(step.steps.map((nested) => namedSteps(nested, generateName))),
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
function namedStepsSwitch(step, generateName) {
|
|
406
|
-
const mainLabel = step.label ?? generateName('switch');
|
|
407
|
-
const namedBranches = step.branches.map((branch) => ({
|
|
408
|
-
condition: branch.condition,
|
|
409
|
-
steps: branch.steps.map((nested) => namedSteps(nested, generateName)),
|
|
410
|
-
next: branch.next,
|
|
411
|
-
}));
|
|
412
|
-
return {
|
|
413
|
-
name: mainLabel,
|
|
414
|
-
step: new SwitchStepASTNamed(namedBranches),
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function namedStepsTry(step, generateName) {
|
|
418
|
-
const mainLabel = step.label ?? generateName('try');
|
|
419
|
-
const namedTrySteps = step.trySteps.map((nested) => namedSteps(nested, generateName));
|
|
420
|
-
const namedExceptSteps = step.exceptSteps?.map((nested) => namedSteps(nested, generateName));
|
|
421
|
-
return {
|
|
422
|
-
name: mainLabel,
|
|
423
|
-
step: new TryStepASTNamed(namedTrySteps, namedExceptSteps, step.retryPolicy, step.errorMap),
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Returns the nested steps in contained in this step.
|
|
428
|
-
*
|
|
429
|
-
* Used in iterating the AST tree.
|
|
430
|
-
*/
|
|
431
|
-
export function nestedSteps(step) {
|
|
432
|
-
switch (step.tag) {
|
|
433
|
-
case 'assign':
|
|
434
|
-
case 'call':
|
|
435
|
-
case 'next':
|
|
436
|
-
case 'raise':
|
|
437
|
-
case 'return':
|
|
438
|
-
case 'jumptarget':
|
|
439
|
-
return [];
|
|
440
|
-
case 'for':
|
|
441
|
-
case 'steps':
|
|
442
|
-
return [step.steps];
|
|
443
|
-
case 'parallel':
|
|
444
|
-
return nestedStepsParallel(step);
|
|
445
|
-
case 'switch':
|
|
446
|
-
return step.conditions.map((x) => x.steps);
|
|
447
|
-
case 'try':
|
|
448
|
-
return nestedStepsTry(step);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
function nestedStepsParallel(step) {
|
|
452
|
-
const nested = [];
|
|
453
|
-
if (step.branches && step.branches.length > 0) {
|
|
454
|
-
// return each branch as a separate child
|
|
455
|
-
nested.push(...step.branches.map((x) => [x]));
|
|
456
|
-
}
|
|
457
|
-
if (step.forStep?.steps && step.forStep.steps.length > 0) {
|
|
458
|
-
nested.push(step.forStep.steps);
|
|
459
|
-
}
|
|
460
|
-
return nested;
|
|
461
|
-
}
|
|
462
|
-
function nestedStepsTry(step) {
|
|
463
|
-
const nested = [];
|
|
464
|
-
if (step.trySteps.length > 0) {
|
|
465
|
-
nested.push(step.trySteps);
|
|
466
|
-
}
|
|
467
|
-
if (step.exceptSteps) {
|
|
468
|
-
nested.push(step.exceptSteps);
|
|
469
|
-
}
|
|
470
|
-
return nested;
|
|
471
|
-
}
|
|
472
5
|
/**
|
|
473
6
|
* Returns an GCP Workflows object representation of a step.
|
|
474
7
|
*/
|
|
475
8
|
export function renderStep(step) {
|
|
476
9
|
switch (step.tag) {
|
|
477
10
|
case 'assign':
|
|
478
|
-
return {
|
|
479
|
-
assign: step.assignments.map(({ name, value }) => {
|
|
480
|
-
return {
|
|
481
|
-
[name.toString()]: expressionToLiteralValueOrLiteralExpression(value),
|
|
482
|
-
};
|
|
483
|
-
}),
|
|
484
|
-
...(step.next && { next: step.next }),
|
|
485
|
-
};
|
|
11
|
+
return { [step.label]: renderAssignStep(step) };
|
|
486
12
|
case 'call':
|
|
487
|
-
return renderCallStep(step);
|
|
13
|
+
return { [step.label]: renderCallStep(step) };
|
|
488
14
|
case 'for':
|
|
489
|
-
return {
|
|
490
|
-
for: renderForBody(step),
|
|
491
|
-
};
|
|
492
|
-
case 'parallel':
|
|
493
|
-
return {
|
|
494
|
-
parallel: {
|
|
495
|
-
...(step.shared && { shared: step.shared }),
|
|
496
|
-
...(step.concurrenceLimit !== undefined && {
|
|
497
|
-
concurrency_limit: step.concurrenceLimit,
|
|
498
|
-
}),
|
|
499
|
-
...(step.exceptionPolicy !== undefined && {
|
|
500
|
-
exception_policy: step.exceptionPolicy,
|
|
501
|
-
}),
|
|
502
|
-
...(step.branches && { branches: renderSteps(step.branches) }),
|
|
503
|
-
...(step.forStep && { for: renderForBody(step.forStep) }),
|
|
504
|
-
},
|
|
505
|
-
};
|
|
15
|
+
return { [step.label]: renderForStep(step) };
|
|
506
16
|
case 'next':
|
|
507
|
-
return {
|
|
508
|
-
|
|
509
|
-
};
|
|
17
|
+
return { [step.label]: { next: step.next } };
|
|
18
|
+
case 'parallel':
|
|
19
|
+
return { [step.label]: renderParallelStep(step) };
|
|
20
|
+
case 'parallel-for':
|
|
21
|
+
return { [step.label]: renderParallelIterationStep(step) };
|
|
510
22
|
case 'raise':
|
|
511
|
-
return {
|
|
512
|
-
raise: expressionToLiteralValueOrLiteralExpression(step.value),
|
|
513
|
-
};
|
|
23
|
+
return { [step.label]: renderRaiseStep(step) };
|
|
514
24
|
case 'return':
|
|
515
|
-
return renderReturnStep(step);
|
|
516
|
-
case 'steps':
|
|
517
|
-
return {
|
|
518
|
-
steps: renderSteps(step.steps),
|
|
519
|
-
};
|
|
25
|
+
return { [step.label]: renderReturnStep(step) };
|
|
520
26
|
case 'switch':
|
|
521
|
-
return {
|
|
522
|
-
switch: step.conditions.map(renderSwitchCondition),
|
|
523
|
-
...(step.next && { next: step.next }),
|
|
524
|
-
};
|
|
27
|
+
return { [step.label]: renderSwitchStep(step) };
|
|
525
28
|
case 'try':
|
|
526
|
-
return renderTryStep(step);
|
|
527
|
-
case '
|
|
29
|
+
return { [step.label]: renderTryStep(step) };
|
|
30
|
+
case 'jump-target':
|
|
31
|
+
// Should not be reached
|
|
528
32
|
return {};
|
|
529
33
|
}
|
|
530
34
|
}
|
|
531
|
-
function
|
|
35
|
+
function renderSteps(steps) {
|
|
36
|
+
return steps.map(renderStep);
|
|
37
|
+
}
|
|
38
|
+
function renderAssignStep(step) {
|
|
532
39
|
return {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
40
|
+
assign: step.assignments.map(({ name, value }) => {
|
|
41
|
+
return {
|
|
42
|
+
[expressionToString(name)]: expressionToLiteralValueOrLiteralExpression(value),
|
|
43
|
+
};
|
|
44
|
+
}),
|
|
45
|
+
...(step.next !== undefined && { next: step.next }),
|
|
536
46
|
};
|
|
537
47
|
}
|
|
538
48
|
function renderCallStep(step) {
|
|
@@ -546,6 +56,11 @@ function renderCallStep(step) {
|
|
|
546
56
|
...(step.result !== undefined && { result: step.result }),
|
|
547
57
|
};
|
|
548
58
|
}
|
|
59
|
+
function renderForStep(step) {
|
|
60
|
+
return {
|
|
61
|
+
for: renderForBody(step),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
549
64
|
function renderForBody(step) {
|
|
550
65
|
let range;
|
|
551
66
|
let inValue;
|
|
@@ -571,13 +86,56 @@ function renderForBody(step) {
|
|
|
571
86
|
};
|
|
572
87
|
}
|
|
573
88
|
function renderForRangeLimit(value) {
|
|
574
|
-
if (value === undefined
|
|
575
|
-
return
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
else if (typeof value === 'number') {
|
|
93
|
+
return value;
|
|
576
94
|
}
|
|
577
95
|
else {
|
|
578
96
|
return expressionToLiteralValueOrLiteralExpression(value);
|
|
579
97
|
}
|
|
580
98
|
}
|
|
99
|
+
function renderParallelStep(step) {
|
|
100
|
+
const renderedBranches = {
|
|
101
|
+
branches: step.branches.map(({ name, steps }) => ({
|
|
102
|
+
[name]: {
|
|
103
|
+
steps: renderSteps(steps),
|
|
104
|
+
},
|
|
105
|
+
})),
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
parallel: {
|
|
109
|
+
...renderedBranches,
|
|
110
|
+
...(step.shared && { shared: step.shared }),
|
|
111
|
+
...(step.concurrencyLimit !== undefined && {
|
|
112
|
+
concurrency_limit: step.concurrencyLimit,
|
|
113
|
+
}),
|
|
114
|
+
...(step.exceptionPolicy !== undefined && {
|
|
115
|
+
exception_policy: step.exceptionPolicy,
|
|
116
|
+
}),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function renderParallelIterationStep(step) {
|
|
121
|
+
return {
|
|
122
|
+
parallel: {
|
|
123
|
+
for: renderForBody(step.forStep),
|
|
124
|
+
...(step.shared && { shared: step.shared }),
|
|
125
|
+
...(step.concurrencyLimit !== undefined && {
|
|
126
|
+
concurrency_limit: step.concurrencyLimit,
|
|
127
|
+
}),
|
|
128
|
+
...(step.exceptionPolicy !== undefined && {
|
|
129
|
+
exception_policy: step.exceptionPolicy,
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function renderRaiseStep(step) {
|
|
135
|
+
return {
|
|
136
|
+
raise: expressionToLiteralValueOrLiteralExpression(step.value),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
581
139
|
function renderReturnStep(step) {
|
|
582
140
|
if (step.value) {
|
|
583
141
|
return {
|
|
@@ -590,6 +148,20 @@ function renderReturnStep(step) {
|
|
|
590
148
|
};
|
|
591
149
|
}
|
|
592
150
|
}
|
|
151
|
+
function renderSwitchStep(step) {
|
|
152
|
+
return {
|
|
153
|
+
switch: step.branches.map(renderSwitchBranch),
|
|
154
|
+
...(step.next !== undefined && { next: step.next }),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function renderSwitchBranch(branch) {
|
|
158
|
+
const includeSteps = (branch.steps ?? []).length > 0 || branch.next === undefined;
|
|
159
|
+
return {
|
|
160
|
+
condition: expressionToLiteralValueOrLiteralExpression(branch.condition),
|
|
161
|
+
...(includeSteps && { steps: renderSteps(branch.steps ?? []) }),
|
|
162
|
+
...(branch.next !== undefined && { next: branch.next }),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
593
165
|
function renderTryStep(step) {
|
|
594
166
|
let retry;
|
|
595
167
|
if (typeof step.retryPolicy === 'undefined') {
|
|
@@ -640,8 +212,777 @@ function renderTryStep(step) {
|
|
|
640
212
|
...(except && { except }),
|
|
641
213
|
};
|
|
642
214
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Convert a IR subworkflow to WorkflowSteps.
|
|
217
|
+
*
|
|
218
|
+
* This function also assigns step labels.
|
|
219
|
+
*/
|
|
220
|
+
export function toStepSubworkflow(ast, generateLabel) {
|
|
221
|
+
const steps = fixJumpLabels(statementListToSteps(generateLabel, {}, ast.statements));
|
|
222
|
+
return new Subworkflow(ast.name, steps, ast.params);
|
|
223
|
+
}
|
|
224
|
+
const statementListToSteps = R.curry(function (generateLabel, ctx, statements) {
|
|
225
|
+
return mergeNextStep(statements.flatMap((s) => statementToSteps(generateLabel, ctx, s)));
|
|
226
|
+
});
|
|
227
|
+
function statementToSteps(generateLabel, ctx, statement) {
|
|
228
|
+
switch (statement.tag) {
|
|
229
|
+
case 'assign':
|
|
230
|
+
return [
|
|
231
|
+
{
|
|
232
|
+
tag: statement.tag,
|
|
233
|
+
label: generateLabel(statement.tag),
|
|
234
|
+
assignments: statement.assignments,
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
case 'break':
|
|
238
|
+
return [breakStep(generateLabel, ctx, statement)];
|
|
239
|
+
case 'continue':
|
|
240
|
+
return [continueStep(generateLabel, ctx, statement)];
|
|
241
|
+
case 'do-while':
|
|
242
|
+
return doWhileSteps(generateLabel, ctx, statement);
|
|
243
|
+
case 'for':
|
|
244
|
+
return [forStep(generateLabel, ctx, statement)];
|
|
245
|
+
case 'for-range':
|
|
246
|
+
return [forRangeStep(generateLabel, ctx, statement)];
|
|
247
|
+
case 'function-invocation':
|
|
248
|
+
return [callStep(generateLabel, statement)];
|
|
249
|
+
case 'if':
|
|
250
|
+
return [ifStep(generateLabel, ctx, statement)];
|
|
251
|
+
case 'label':
|
|
252
|
+
return labelledSteps(generateLabel, ctx, statement);
|
|
253
|
+
case 'parallel-for':
|
|
254
|
+
return [parallelIterationStep(generateLabel, ctx, statement)];
|
|
255
|
+
case 'parallel':
|
|
256
|
+
return [parallelStep(generateLabel, ctx, statement)];
|
|
257
|
+
case 'raise':
|
|
258
|
+
return [
|
|
259
|
+
{
|
|
260
|
+
tag: statement.tag,
|
|
261
|
+
label: generateLabel(statement.tag),
|
|
262
|
+
value: statement.value,
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
case 'return':
|
|
266
|
+
return [returnStep(generateLabel, ctx, statement)];
|
|
267
|
+
case 'switch':
|
|
268
|
+
return switchSteps(generateLabel, ctx, statement);
|
|
269
|
+
case 'try':
|
|
270
|
+
return trySteps(generateLabel, ctx, statement);
|
|
271
|
+
case 'while':
|
|
272
|
+
return whileSteps(generateLabel, ctx, statement);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function callStep(generateLabel, statement) {
|
|
276
|
+
const labelPrefix = 'call_' + statement.callee.replaceAll(/[^a-zA-Z0-9]/g, '_') + '_';
|
|
277
|
+
return {
|
|
278
|
+
tag: 'call',
|
|
279
|
+
label: generateLabel(labelPrefix),
|
|
280
|
+
call: statement.callee,
|
|
281
|
+
args: statement.args,
|
|
282
|
+
result: statement.result,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function breakStep(generateLabel, ctx, statement) {
|
|
286
|
+
if (ctx.finalizerTargets) {
|
|
287
|
+
// TODO: would need to detect if this breaks out of the try or catch block,
|
|
288
|
+
// execute the finally block first and then do the break.
|
|
289
|
+
throw new InternalTranspilingError('break is not supported inside a try-finally block');
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
tag: 'next',
|
|
293
|
+
label: generateLabel('next'),
|
|
294
|
+
next: statement.label ?? ctx.breakTarget ?? 'break',
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function continueStep(generateLabel, ctx, statement) {
|
|
298
|
+
if (ctx.finalizerTargets) {
|
|
299
|
+
// TODO: would need to detect if continue breaks out of the try or catch block,
|
|
300
|
+
// execute the finally block first and then do the continue.
|
|
301
|
+
throw new InternalTranspilingError('continue is not supported inside a try-finally block');
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
tag: 'next',
|
|
305
|
+
label: generateLabel('next'),
|
|
306
|
+
next: statement.label ?? ctx.continueTarget ?? 'continue',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function doWhileSteps(generateLabel, ctx, statement) {
|
|
310
|
+
const startOfLoopLabel = generatePlaceholderLabel();
|
|
311
|
+
const endOfLoopLabel = generatePlaceholderLabel();
|
|
312
|
+
const ctx2 = Object.assign({}, ctx, {
|
|
313
|
+
continueTarget: startOfLoopLabel,
|
|
314
|
+
breakTarget: endOfLoopLabel,
|
|
315
|
+
});
|
|
316
|
+
const toSteps = statementListToSteps(generateLabel, ctx2);
|
|
317
|
+
return [
|
|
318
|
+
{
|
|
319
|
+
tag: 'jump-target',
|
|
320
|
+
label: startOfLoopLabel,
|
|
321
|
+
},
|
|
322
|
+
...toSteps(statement.body),
|
|
323
|
+
{
|
|
324
|
+
tag: 'switch',
|
|
325
|
+
label: generateLabel('switch'),
|
|
326
|
+
branches: [
|
|
327
|
+
{
|
|
328
|
+
condition: statement.condition,
|
|
329
|
+
steps: [],
|
|
330
|
+
next: startOfLoopLabel,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
tag: 'jump-target',
|
|
336
|
+
label: endOfLoopLabel,
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
function whileSteps(generateLabel, ctx, statement) {
|
|
341
|
+
const startOfLoopLabel = generateLabel('switch');
|
|
342
|
+
const endOfLoopLabel = generatePlaceholderLabel();
|
|
343
|
+
const ctx2 = Object.assign({}, ctx, {
|
|
344
|
+
continueTarget: startOfLoopLabel,
|
|
345
|
+
breakTarget: endOfLoopLabel,
|
|
346
|
+
});
|
|
347
|
+
const toSteps = statementListToSteps(generateLabel, ctx2);
|
|
348
|
+
const steps = appendNextStep(generateLabel, toSteps(statement.body), startOfLoopLabel);
|
|
349
|
+
const body = {
|
|
350
|
+
condition: statement.condition,
|
|
351
|
+
steps,
|
|
352
|
+
};
|
|
353
|
+
return [
|
|
354
|
+
{
|
|
355
|
+
tag: 'switch',
|
|
356
|
+
label: startOfLoopLabel,
|
|
357
|
+
branches: [body],
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
tag: 'jump-target',
|
|
361
|
+
label: endOfLoopLabel,
|
|
362
|
+
},
|
|
363
|
+
];
|
|
364
|
+
}
|
|
365
|
+
function forStep(generateLabel, ctx, statement) {
|
|
366
|
+
const label = generateLabel('for');
|
|
367
|
+
const bodyCtx = Object.assign({}, ctx, {
|
|
368
|
+
continueTarget: undefined,
|
|
369
|
+
breakTarget: undefined,
|
|
370
|
+
});
|
|
371
|
+
const toSteps = statementListToSteps(generateLabel, bodyCtx);
|
|
372
|
+
return {
|
|
373
|
+
tag: 'for',
|
|
374
|
+
label,
|
|
375
|
+
steps: toSteps(statement.body),
|
|
376
|
+
loopVariableName: statement.loopVariableName,
|
|
377
|
+
listExpression: statement.listExpression,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function forRangeStep(generateLabel, ctx, statement) {
|
|
381
|
+
const label = generateLabel('for');
|
|
382
|
+
const bodyCtx = Object.assign({}, ctx, {
|
|
383
|
+
continueTarget: undefined,
|
|
384
|
+
breakTarget: undefined,
|
|
385
|
+
});
|
|
386
|
+
const toSteps = statementListToSteps(generateLabel, bodyCtx);
|
|
387
|
+
return {
|
|
388
|
+
tag: 'for',
|
|
389
|
+
label,
|
|
390
|
+
steps: toSteps(statement.body),
|
|
391
|
+
loopVariableName: statement.loopVariableName,
|
|
392
|
+
rangeStart: statement.rangeStart,
|
|
393
|
+
rangeEnd: statement.rangeEnd,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function ifStep(generateLabel, ctx, statement) {
|
|
397
|
+
const label = generateLabel('switch');
|
|
398
|
+
const toSteps = statementListToSteps(generateLabel, ctx);
|
|
399
|
+
const branches = statement.branches.map((branch) => {
|
|
400
|
+
if ('body' in branch) {
|
|
401
|
+
// Use the shorter "next" form on break and continue statements
|
|
402
|
+
if (branch.body.length === 1 && branch.body[0].tag === 'break') {
|
|
403
|
+
return {
|
|
404
|
+
condition: branch.condition,
|
|
405
|
+
next: breakStep(generateLabel, ctx, branch.body[0]).next,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
else if (branch.body.length === 1 &&
|
|
409
|
+
branch.body[0].tag === 'continue') {
|
|
410
|
+
return {
|
|
411
|
+
condition: branch.condition,
|
|
412
|
+
next: continueStep(generateLabel, ctx, branch.body[0]).next,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
return {
|
|
417
|
+
condition: branch.condition,
|
|
418
|
+
steps: toSteps(branch.body),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
return {
|
|
424
|
+
condition: branch.condition,
|
|
425
|
+
next: branch.next,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
646
428
|
});
|
|
429
|
+
return {
|
|
430
|
+
tag: 'switch',
|
|
431
|
+
label,
|
|
432
|
+
branches,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function labelledSteps(generateLabel, ctx, statement) {
|
|
436
|
+
const toSteps = statementListToSteps(generateLabel, ctx);
|
|
437
|
+
const steps = toSteps(statement.statements);
|
|
438
|
+
if (steps.length >= 1) {
|
|
439
|
+
// FIXME: do not first generate a label and then immediately overwrite it here
|
|
440
|
+
steps[0].label = statement.label;
|
|
441
|
+
}
|
|
442
|
+
return steps;
|
|
443
|
+
}
|
|
444
|
+
function parallelStep(generateLabel, ctx, statement) {
|
|
445
|
+
const label = generateLabel('parallel');
|
|
446
|
+
const toSteps = statementListToSteps(generateLabel, ctx);
|
|
447
|
+
const branches = statement.branches.map((branch) => ({
|
|
448
|
+
name: branch.name,
|
|
449
|
+
steps: toSteps(branch.body),
|
|
450
|
+
}));
|
|
451
|
+
return {
|
|
452
|
+
tag: 'parallel',
|
|
453
|
+
label,
|
|
454
|
+
branches,
|
|
455
|
+
shared: statement.shared,
|
|
456
|
+
concurrencyLimit: statement.concurrencyLimit,
|
|
457
|
+
exceptionPolicy: statement.exceptionPolicy,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function parallelIterationStep(generateLabel, ctx, statement) {
|
|
461
|
+
const label = generateLabel('parallel');
|
|
462
|
+
const innerStep = statement.forStep.tag === 'for'
|
|
463
|
+
? forStep(generateLabel, ctx, statement.forStep)
|
|
464
|
+
: forRangeStep(generateLabel, ctx, statement.forStep);
|
|
465
|
+
return {
|
|
466
|
+
tag: 'parallel-for',
|
|
467
|
+
label,
|
|
468
|
+
forStep: innerStep,
|
|
469
|
+
shared: statement.shared,
|
|
470
|
+
concurrencyLimit: statement.concurrencyLimit,
|
|
471
|
+
exceptionPolicy: statement.exceptionPolicy,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function returnStep(generateLabel, ctx, statement) {
|
|
475
|
+
if (ctx.finalizerTargets && ctx.finalizerTargets.length > 0) {
|
|
476
|
+
// If we are in try statement with a finally block, return statements are
|
|
477
|
+
// replaced by a jump to finally back with a captured return value.
|
|
478
|
+
return delayedReturnAndJumpToFinalizer(generateLabel, ctx, statement.value);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
return {
|
|
482
|
+
tag: 'return',
|
|
483
|
+
label: generateLabel('return'),
|
|
484
|
+
value: statement.value,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function delayedReturnAndJumpToFinalizer(generateLabel, ctx, value) {
|
|
489
|
+
const finalizerTarget = ctx.finalizerTargets && ctx.finalizerTargets.length > 0
|
|
490
|
+
? ctx.finalizerTargets[ctx.finalizerTargets.length - 1]
|
|
491
|
+
: undefined;
|
|
492
|
+
const [conditionVariable, valueVariable] = finalizerVariables(ctx);
|
|
493
|
+
return {
|
|
494
|
+
tag: 'assign',
|
|
495
|
+
label: generateLabel('assign'),
|
|
496
|
+
assignments: [
|
|
497
|
+
{
|
|
498
|
+
name: variableReferenceEx(conditionVariable),
|
|
499
|
+
value: stringEx('return'),
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: variableReferenceEx(valueVariable),
|
|
503
|
+
value: value ?? nullEx,
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
next: finalizerTarget,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function switchSteps(generateLabel, ctx, statement) {
|
|
510
|
+
const label = generateLabel('switch');
|
|
511
|
+
const endOfSwitchLabel = generatePlaceholderLabel();
|
|
512
|
+
const switchCtx = Object.assign({}, ctx, { breakTarget: endOfSwitchLabel });
|
|
513
|
+
const toSteps = statementListToSteps(generateLabel, switchCtx);
|
|
514
|
+
const steps = [];
|
|
515
|
+
const branches = [];
|
|
516
|
+
// prepare steps for each branch so that we will have jump targets ready even for
|
|
517
|
+
// fall-through branches that need to jump to the next branch
|
|
518
|
+
const branchSteps = statement.branches.map((branch) => toSteps(branch.body));
|
|
519
|
+
steps.push(...branchSteps.flat());
|
|
520
|
+
statement.branches.forEach((branch, i) => {
|
|
521
|
+
// search forward until the next non-fall-through branch (= a branch that has
|
|
522
|
+
// non-empty steps)
|
|
523
|
+
const jumpTarget = branchSteps.slice(i).find((s) => s.length > 0);
|
|
524
|
+
const next = jumpTarget?.[0]?.label ?? endOfSwitchLabel;
|
|
525
|
+
branches.push({
|
|
526
|
+
condition: branch.condition,
|
|
527
|
+
next,
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
steps.unshift({
|
|
531
|
+
tag: 'switch',
|
|
532
|
+
label,
|
|
533
|
+
branches,
|
|
534
|
+
});
|
|
535
|
+
steps.push({
|
|
536
|
+
tag: 'jump-target',
|
|
537
|
+
label: endOfSwitchLabel,
|
|
538
|
+
});
|
|
539
|
+
return steps;
|
|
540
|
+
}
|
|
541
|
+
function trySteps(generateLabel, ctx, statement) {
|
|
542
|
+
if (!statement.finalizerBody) {
|
|
543
|
+
// Basic try-catch without a finally block
|
|
544
|
+
return [tryCatchRetrySteps(generateLabel, ctx, statement)];
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
// Try-finally is translated to two nested try blocks. The innermost try is
|
|
548
|
+
// the actual try body with control flow statements (return, in the future
|
|
549
|
+
// also break/continue) replaced by jumps to the finally block.
|
|
550
|
+
//
|
|
551
|
+
// The outer try's catch block saves the exception and continues to the
|
|
552
|
+
// finally block.
|
|
553
|
+
//
|
|
554
|
+
// The nested try blocks are followed by the finally block and a switch for
|
|
555
|
+
// checking if we need to perform a delayed return/raise.
|
|
556
|
+
const startOfFinalizer = {
|
|
557
|
+
tag: 'jump-target',
|
|
558
|
+
label: generatePlaceholderLabel(),
|
|
559
|
+
};
|
|
560
|
+
const targets = ctx.finalizerTargets ?? [];
|
|
561
|
+
targets.push(startOfFinalizer.label);
|
|
562
|
+
ctx = Object.assign({}, ctx, { finalizerTargets: targets });
|
|
563
|
+
const [conditionVariable, valueVariable] = finalizerVariables(ctx);
|
|
564
|
+
const initStatement = finalizerInitializer(generateLabel, conditionVariable, valueVariable);
|
|
565
|
+
const outerLabel = generateLabel('try');
|
|
566
|
+
const innerTry = tryCatchRetrySteps(generateLabel, ctx, statement);
|
|
567
|
+
const outerTry = {
|
|
568
|
+
tag: 'try',
|
|
569
|
+
label: outerLabel,
|
|
570
|
+
trySteps: [innerTry],
|
|
571
|
+
exceptSteps: finalizerDelayedException(generateLabel, '__fin_exc', conditionVariable, valueVariable),
|
|
572
|
+
errorMap: '__fin_exc',
|
|
573
|
+
};
|
|
574
|
+
// Reset ctx before parsing the finally block because we don't want to
|
|
575
|
+
// transform returns in finally block in to delayed returns
|
|
576
|
+
if (ctx.finalizerTargets && ctx.finalizerTargets.length <= 1) {
|
|
577
|
+
delete ctx.finalizerTargets;
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
ctx.finalizerTargets?.pop();
|
|
581
|
+
}
|
|
582
|
+
const toSteps = statementListToSteps(generateLabel, ctx);
|
|
583
|
+
const finallyBlock = toSteps(statement.finalizerBody);
|
|
584
|
+
return [
|
|
585
|
+
initStatement,
|
|
586
|
+
outerTry,
|
|
587
|
+
startOfFinalizer,
|
|
588
|
+
...finallyBlock,
|
|
589
|
+
finalizerFooter(generateLabel, conditionVariable, valueVariable),
|
|
590
|
+
];
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function tryCatchRetrySteps(generateLabel, ctx, statement) {
|
|
594
|
+
const toSteps = statementListToSteps(generateLabel, ctx);
|
|
595
|
+
return {
|
|
596
|
+
tag: 'try',
|
|
597
|
+
label: generateLabel('try'),
|
|
598
|
+
trySteps: toSteps(statement.tryBody),
|
|
599
|
+
exceptSteps: statement.exceptBody
|
|
600
|
+
? toSteps(statement.exceptBody)
|
|
601
|
+
: undefined,
|
|
602
|
+
retryPolicy: statement.retryPolicy,
|
|
603
|
+
errorMap: statement.errorMap,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function finalizerVariables(ctx) {
|
|
607
|
+
const targets = ctx.finalizerTargets ?? [];
|
|
608
|
+
const nestingLevel = targets.length > 0 ? `${targets.length}` : '';
|
|
609
|
+
const conditionVariable = `__t2w_finally_condition${nestingLevel}`;
|
|
610
|
+
const valueVariable = `__t2w_finally_value${nestingLevel}`;
|
|
611
|
+
return [conditionVariable, valueVariable];
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* The shared header for try-finally for initializing the temp variables
|
|
615
|
+
*/
|
|
616
|
+
function finalizerInitializer(generateLabel, conditionVariable, valueVariable) {
|
|
617
|
+
return {
|
|
618
|
+
tag: 'assign',
|
|
619
|
+
label: generateLabel('assign'),
|
|
620
|
+
assignments: [
|
|
621
|
+
{
|
|
622
|
+
name: variableReferenceEx(conditionVariable),
|
|
623
|
+
value: nullEx,
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
name: variableReferenceEx(valueVariable),
|
|
627
|
+
value: nullEx,
|
|
628
|
+
},
|
|
629
|
+
],
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* The shared footer of a finally block that re-throws the exception or
|
|
634
|
+
* returns the value returned by the try body.
|
|
635
|
+
*
|
|
636
|
+
* The footer code in TypeScript:
|
|
637
|
+
*
|
|
638
|
+
* if (__t2w_finally_condition == "return") {
|
|
639
|
+
* return __t2w_finally_value
|
|
640
|
+
* } elseif (__t2w_finally_condition == "raise") {
|
|
641
|
+
* throw __t2w_finally_value
|
|
642
|
+
* }
|
|
643
|
+
*/
|
|
644
|
+
function finalizerFooter(generateLabel, conditionVariable, valueVariable) {
|
|
645
|
+
const variable = variableReferenceEx(conditionVariable);
|
|
646
|
+
const val = variableReferenceEx(valueVariable);
|
|
647
|
+
const returnString = stringEx('return');
|
|
648
|
+
const raiseString = stringEx('raise');
|
|
649
|
+
return {
|
|
650
|
+
tag: 'switch',
|
|
651
|
+
label: generateLabel('switch'),
|
|
652
|
+
branches: [
|
|
653
|
+
{
|
|
654
|
+
condition: binaryEx(variable, '==', returnString),
|
|
655
|
+
steps: [{ tag: 'return', label: generateLabel('return'), value: val }],
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
condition: binaryEx(variable, '==', raiseString),
|
|
659
|
+
steps: [{ tag: 'raise', label: generateLabel('raise'), value: val }],
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function finalizerDelayedException(generateLabel, exceptionVariableName, conditionVariableName, valueVariableName) {
|
|
665
|
+
return [
|
|
666
|
+
{
|
|
667
|
+
tag: 'assign',
|
|
668
|
+
label: generateLabel('assign'),
|
|
669
|
+
assignments: [
|
|
670
|
+
{
|
|
671
|
+
name: variableReferenceEx(conditionVariableName),
|
|
672
|
+
value: stringEx('raise'),
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: variableReferenceEx(valueVariableName),
|
|
676
|
+
value: variableReferenceEx(exceptionVariableName),
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
},
|
|
680
|
+
];
|
|
681
|
+
}
|
|
682
|
+
function generatePlaceholderLabel() {
|
|
683
|
+
return `jumptarget_${Math.floor(Math.random() * 2 ** 32).toString(16)}`;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Merge a next step to the previous step.
|
|
687
|
+
*
|
|
688
|
+
* For example, transforms this:
|
|
689
|
+
*
|
|
690
|
+
* - assign1:
|
|
691
|
+
* assign:
|
|
692
|
+
* - a: 1
|
|
693
|
+
* - next1:
|
|
694
|
+
* next: target1
|
|
695
|
+
*
|
|
696
|
+
* into this:
|
|
697
|
+
*
|
|
698
|
+
* - assign1:
|
|
699
|
+
* assign:
|
|
700
|
+
* - a: 1
|
|
701
|
+
* next: target1
|
|
702
|
+
*/
|
|
703
|
+
function mergeNextStep(steps) {
|
|
704
|
+
return steps.reduce((acc, step) => {
|
|
705
|
+
const prev = acc.at(-1);
|
|
706
|
+
if (step.tag === 'next' && prev?.tag === 'assign') {
|
|
707
|
+
// TODO: This will delete the "next" step that already had a label. The
|
|
708
|
+
// output will have a hole in label number. How to avoid that?
|
|
709
|
+
acc.pop();
|
|
710
|
+
acc.push({ ...prev, next: step.next });
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
acc.push(step);
|
|
714
|
+
}
|
|
715
|
+
return acc;
|
|
716
|
+
}, []);
|
|
717
|
+
}
|
|
718
|
+
function appendNextStep(generateLabel, steps, nextLabel) {
|
|
719
|
+
const last = steps.at(-1);
|
|
720
|
+
if (last?.tag === 'assign') {
|
|
721
|
+
steps.pop();
|
|
722
|
+
steps.push({ ...last, next: nextLabel });
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
steps.push({
|
|
726
|
+
tag: 'next',
|
|
727
|
+
label: generateLabel('next'),
|
|
728
|
+
next: nextLabel,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
return steps;
|
|
732
|
+
}
|
|
733
|
+
function fixJumpLabels(steps) {
|
|
734
|
+
const jumpTargetLabels = collectActualJumpTargets(steps);
|
|
735
|
+
const stepsWithoutJumpTargetNodes = removeJumpTargetSteps(steps);
|
|
736
|
+
const relabeledSteps = relabelNextLabels(stepsWithoutJumpTargetNodes, jumpTargetLabels);
|
|
737
|
+
return relabeledSteps;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Find a mapping from jump target labels to step names.
|
|
741
|
+
*
|
|
742
|
+
* Iterate over all steps in the workflow. For JumpTargetAST nodes, find the
|
|
743
|
+
* next node that is not a JumpTargetAST, save its name as the real jump taget
|
|
744
|
+
* name.
|
|
745
|
+
*/
|
|
746
|
+
function collectActualJumpTargets(steps) {
|
|
747
|
+
const replacements = new Map();
|
|
748
|
+
// The processing is done iteratively with an explicit stack because
|
|
749
|
+
// nextNonJumpTargetNode() needs the stack. Note the order of steps on the
|
|
750
|
+
// stack: the first step is the last element of the stack.
|
|
751
|
+
const stack = [];
|
|
752
|
+
stack.push(...stepsToJumpStackElements(steps, 0));
|
|
753
|
+
while (stack.length > 0) {
|
|
754
|
+
// Do not yet pop in case nextNonJumpTargetNode needs to search the stack
|
|
755
|
+
const { step, nestingLevel } = stack[stack.length - 1];
|
|
756
|
+
if (step.tag === 'jump-target') {
|
|
757
|
+
const currentLabel = step.label;
|
|
758
|
+
const target = nextNonJumpTargetNode(stack);
|
|
759
|
+
const targetName = target ? target.label : 'end';
|
|
760
|
+
replacements.set(currentLabel, targetName);
|
|
761
|
+
}
|
|
762
|
+
// Now nextNonJumpTargetNode has been executed and it's safe to pop the
|
|
763
|
+
// current element from the stack.
|
|
764
|
+
stack.pop();
|
|
765
|
+
const children = nestedSteps(step).map((x) => stepsToJumpStackElements(x, nestingLevel + 1));
|
|
766
|
+
children.reverse();
|
|
767
|
+
children.forEach((nestedChildren) => {
|
|
768
|
+
stack.push(...nestedChildren);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
return replacements;
|
|
772
|
+
}
|
|
773
|
+
function stepsToJumpStackElements(steps, nestingLevel) {
|
|
774
|
+
const block = steps.map((step, i) => ({
|
|
775
|
+
step,
|
|
776
|
+
nestingLevel,
|
|
777
|
+
isLastInBlock: i === steps.length - 1,
|
|
778
|
+
}));
|
|
779
|
+
block.reverse();
|
|
780
|
+
return block;
|
|
781
|
+
}
|
|
782
|
+
function nextNonJumpTargetNode(stack) {
|
|
783
|
+
let nestingLevel = stack[stack.length - 1]?.nestingLevel ?? -1;
|
|
784
|
+
while (nestingLevel >= 0) {
|
|
785
|
+
// Consider only the steps in the current code block (= the same nesting
|
|
786
|
+
// level, taking steps until isLastInBlock)
|
|
787
|
+
const endOfBlockIndex = stack.findLastIndex((x) => x.nestingLevel === nestingLevel && x.isLastInBlock);
|
|
788
|
+
if (endOfBlockIndex < 0) {
|
|
789
|
+
// should not be reached
|
|
790
|
+
throw new InternalTranspilingError('Failed to find end of block in nextNonJumpTargetNode');
|
|
791
|
+
}
|
|
792
|
+
const firstNonJumpTarget = stack
|
|
793
|
+
.slice(endOfBlockIndex)
|
|
794
|
+
.findLast((x) => x.nestingLevel === nestingLevel && x.step.tag !== 'jump-target');
|
|
795
|
+
if (firstNonJumpTarget) {
|
|
796
|
+
return firstNonJumpTarget.step;
|
|
797
|
+
}
|
|
798
|
+
nestingLevel--;
|
|
799
|
+
}
|
|
800
|
+
return undefined;
|
|
801
|
+
}
|
|
802
|
+
function nestedSteps(step) {
|
|
803
|
+
switch (step.tag) {
|
|
804
|
+
case 'assign':
|
|
805
|
+
case 'call':
|
|
806
|
+
case 'next':
|
|
807
|
+
case 'raise':
|
|
808
|
+
case 'return':
|
|
809
|
+
case 'jump-target':
|
|
810
|
+
return [];
|
|
811
|
+
case 'for':
|
|
812
|
+
return [step.steps];
|
|
813
|
+
case 'parallel':
|
|
814
|
+
return step.branches.map((x) => x.steps);
|
|
815
|
+
case 'parallel-for':
|
|
816
|
+
return [step.forStep.steps];
|
|
817
|
+
case 'switch':
|
|
818
|
+
return step.branches.map((x) => x.steps ?? []);
|
|
819
|
+
case 'try':
|
|
820
|
+
return nestedStepsTry(step);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
function nestedStepsTry(step) {
|
|
824
|
+
const nested = [];
|
|
825
|
+
if (step.trySteps.length > 0) {
|
|
826
|
+
nested.push(step.trySteps);
|
|
827
|
+
}
|
|
828
|
+
if (step.exceptSteps) {
|
|
829
|
+
nested.push(step.exceptSteps);
|
|
830
|
+
}
|
|
831
|
+
return nested;
|
|
832
|
+
}
|
|
833
|
+
function removeJumpTargetSteps(steps) {
|
|
834
|
+
return steps
|
|
835
|
+
.filter((x) => x.tag !== 'jump-target')
|
|
836
|
+
.map(removeJumpTargetRecurse);
|
|
837
|
+
}
|
|
838
|
+
function removeJumpTargetRecurse(step) {
|
|
839
|
+
switch (step.tag) {
|
|
840
|
+
case 'assign':
|
|
841
|
+
case 'call':
|
|
842
|
+
case 'next':
|
|
843
|
+
case 'raise':
|
|
844
|
+
case 'return':
|
|
845
|
+
case 'jump-target':
|
|
846
|
+
return step;
|
|
847
|
+
case 'for':
|
|
848
|
+
return removeJumpTargetsFor(step);
|
|
849
|
+
case 'parallel':
|
|
850
|
+
return removeJumpTargetsParallel(step);
|
|
851
|
+
case 'parallel-for':
|
|
852
|
+
return removeJumpTargetsParallelIteration(step);
|
|
853
|
+
case 'switch':
|
|
854
|
+
return removeJumpTargetsSwitch(step);
|
|
855
|
+
case 'try':
|
|
856
|
+
return removeJumpTargetTry(step);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function removeJumpTargetsFor(step) {
|
|
860
|
+
return {
|
|
861
|
+
...step,
|
|
862
|
+
steps: removeJumpTargetSteps(step.steps),
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function removeJumpTargetsParallel(step) {
|
|
866
|
+
const branches = step.branches.map(({ name, steps: nestedSteps }) => ({
|
|
867
|
+
name,
|
|
868
|
+
steps: removeJumpTargetSteps(nestedSteps),
|
|
869
|
+
}));
|
|
870
|
+
return {
|
|
871
|
+
...step,
|
|
872
|
+
branches,
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
function removeJumpTargetsParallelIteration(step) {
|
|
876
|
+
return {
|
|
877
|
+
...step,
|
|
878
|
+
forStep: removeJumpTargetsFor(step.forStep),
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function removeJumpTargetsSwitch(step) {
|
|
882
|
+
const transformedBranches = step.branches.map((branch) => {
|
|
883
|
+
return {
|
|
884
|
+
condition: branch.condition,
|
|
885
|
+
steps: branch.steps !== undefined
|
|
886
|
+
? removeJumpTargetSteps(branch.steps)
|
|
887
|
+
: undefined,
|
|
888
|
+
next: branch.next,
|
|
889
|
+
};
|
|
890
|
+
});
|
|
891
|
+
return {
|
|
892
|
+
...step,
|
|
893
|
+
branches: transformedBranches,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function removeJumpTargetTry(step) {
|
|
897
|
+
return {
|
|
898
|
+
...step,
|
|
899
|
+
trySteps: removeJumpTargetSteps(step.trySteps),
|
|
900
|
+
exceptSteps: step.exceptSteps !== undefined
|
|
901
|
+
? removeJumpTargetSteps(step.exceptSteps)
|
|
902
|
+
: undefined,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function relabelNextLabels(steps, replacements) {
|
|
906
|
+
return steps.map((step) => renameJumpTargets(step, replacements));
|
|
907
|
+
}
|
|
908
|
+
function renameJumpTargets(step, replaceLabels) {
|
|
909
|
+
switch (step.tag) {
|
|
910
|
+
case 'call':
|
|
911
|
+
case 'raise':
|
|
912
|
+
case 'return':
|
|
913
|
+
case 'jump-target':
|
|
914
|
+
return step;
|
|
915
|
+
case 'assign':
|
|
916
|
+
return renameJumpTargetsAssign(step, replaceLabels);
|
|
917
|
+
case 'for':
|
|
918
|
+
return renameJumpTargetsFor(step, replaceLabels);
|
|
919
|
+
case 'next':
|
|
920
|
+
return renameJumpTargetsNext(step, replaceLabels);
|
|
921
|
+
case 'parallel':
|
|
922
|
+
return renameJumpTargetsParallel(step, replaceLabels);
|
|
923
|
+
case 'parallel-for':
|
|
924
|
+
return renameJumpTargetsParallelIteration(step, replaceLabels);
|
|
925
|
+
case 'switch':
|
|
926
|
+
return renameJumpTargetsSwitch(step, replaceLabels);
|
|
927
|
+
case 'try':
|
|
928
|
+
return renameJumpTargetsTry(step, replaceLabels);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
function renameJumpTargetsAssign(step, replaceLabels) {
|
|
932
|
+
if (step.next) {
|
|
933
|
+
const newLabel = replaceLabels.get(step.next);
|
|
934
|
+
if (newLabel) {
|
|
935
|
+
return { ...step, next: newLabel };
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return step;
|
|
939
|
+
}
|
|
940
|
+
function renameJumpTargetsFor(step, replaceLabels) {
|
|
941
|
+
const transformedSteps = step.steps.map((s) => renameJumpTargets(s, replaceLabels));
|
|
942
|
+
return { ...step, steps: transformedSteps };
|
|
943
|
+
}
|
|
944
|
+
function renameJumpTargetsNext(step, replaceLabels) {
|
|
945
|
+
const newLabel = replaceLabels.get(step.next) ?? step.next;
|
|
946
|
+
return { ...step, next: newLabel };
|
|
947
|
+
}
|
|
948
|
+
function renameJumpTargetsParallel(step, replaceLabels) {
|
|
949
|
+
const branches = step.branches.map(({ name, steps }) => ({
|
|
950
|
+
name,
|
|
951
|
+
steps: steps.map((s) => renameJumpTargets(s, replaceLabels)),
|
|
952
|
+
}));
|
|
953
|
+
return { ...step, branches };
|
|
954
|
+
}
|
|
955
|
+
function renameJumpTargetsParallelIteration(step, replaceLabels) {
|
|
956
|
+
return { ...step, forStep: renameJumpTargetsFor(step.forStep, replaceLabels) };
|
|
957
|
+
}
|
|
958
|
+
function renameJumpTargetsSwitch(step, replaceLabels) {
|
|
959
|
+
const updatedNext = step.next
|
|
960
|
+
? (replaceLabels.get(step.next) ?? step.next)
|
|
961
|
+
: undefined;
|
|
962
|
+
const updatedBranches = step.branches.map((cond) => {
|
|
963
|
+
const updatedCondNext = cond.next
|
|
964
|
+
? (replaceLabels.get(cond.next) ?? cond.next)
|
|
965
|
+
: undefined;
|
|
966
|
+
const updatedCondSteps = cond.steps?.map((nested) => renameJumpTargets(nested, replaceLabels));
|
|
967
|
+
return {
|
|
968
|
+
condition: cond.condition,
|
|
969
|
+
steps: updatedCondSteps,
|
|
970
|
+
next: updatedCondNext,
|
|
971
|
+
};
|
|
972
|
+
});
|
|
973
|
+
return {
|
|
974
|
+
tag: 'switch',
|
|
975
|
+
label: step.label,
|
|
976
|
+
branches: updatedBranches,
|
|
977
|
+
next: updatedNext,
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function renameJumpTargetsTry(step, replaceLabels) {
|
|
981
|
+
const transformedTrySteps = step.trySteps.map((nested) => renameJumpTargets(nested, replaceLabels));
|
|
982
|
+
const transformedExceptSteps = step.exceptSteps?.map((nested) => renameJumpTargets(nested, replaceLabels));
|
|
983
|
+
return {
|
|
984
|
+
...step,
|
|
985
|
+
trySteps: transformedTrySteps,
|
|
986
|
+
exceptSteps: transformedExceptSteps,
|
|
987
|
+
};
|
|
647
988
|
}
|