ts2workflows 0.11.0 → 0.13.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +17 -7
  3. package/dist/ast/expressions.d.ts +37 -41
  4. package/dist/ast/expressions.d.ts.map +1 -1
  5. package/dist/ast/expressions.js +124 -255
  6. package/dist/ast/statements.d.ts +132 -0
  7. package/dist/ast/statements.d.ts.map +1 -0
  8. package/dist/ast/statements.js +197 -0
  9. package/dist/ast/steps.d.ts +82 -194
  10. package/dist/ast/steps.d.ts.map +1 -1
  11. package/dist/ast/steps.js +862 -523
  12. package/dist/ast/workflows.d.ts +14 -7
  13. package/dist/ast/workflows.d.ts.map +1 -1
  14. package/dist/ast/workflows.js +19 -10
  15. package/dist/cli.js +45 -32
  16. package/dist/errors.d.ts +4 -0
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/errors.js +7 -0
  19. package/dist/transpiler/index.d.ts +14 -1
  20. package/dist/transpiler/index.d.ts.map +1 -1
  21. package/dist/transpiler/index.js +150 -31
  22. package/dist/transpiler/linker.d.ts +3 -0
  23. package/dist/transpiler/linker.d.ts.map +1 -0
  24. package/dist/transpiler/linker.js +45 -0
  25. package/dist/transpiler/parseexpressions.d.ts +11 -0
  26. package/dist/transpiler/parseexpressions.d.ts.map +1 -0
  27. package/dist/transpiler/{expressions.js → parseexpressions.js} +90 -103
  28. package/dist/transpiler/parsestatement.d.ts +7 -0
  29. package/dist/transpiler/parsestatement.d.ts.map +1 -0
  30. package/dist/transpiler/parsestatement.js +862 -0
  31. package/dist/transpiler/stepnames.d.ts +3 -0
  32. package/dist/transpiler/stepnames.d.ts.map +1 -0
  33. package/dist/transpiler/stepnames.js +17 -0
  34. package/dist/transpiler/transformations.d.ts +3 -19
  35. package/dist/transpiler/transformations.d.ts.map +1 -1
  36. package/dist/transpiler/transformations.js +267 -322
  37. package/language_reference.md +8 -2
  38. package/package.json +13 -7
  39. package/types/workflowslib.d.ts +26 -13
  40. package/dist/ast/stepnames.d.ts +0 -9
  41. package/dist/ast/stepnames.d.ts.map +0 -1
  42. package/dist/ast/stepnames.js +0 -280
  43. package/dist/transpiler/expressions.d.ts +0 -11
  44. package/dist/transpiler/expressions.d.ts.map +0 -1
  45. package/dist/transpiler/statements.d.ts +0 -10
  46. package/dist/transpiler/statements.d.ts.map +0 -1
  47. package/dist/transpiler/statements.js +0 -1098
  48. package/dist/utils.d.ts +0 -2
  49. package/dist/utils.d.ts.map +0 -1
  50. package/dist/utils.js +0 -3
@@ -0,0 +1,862 @@
1
+ import * as R from 'ramda';
2
+ import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
3
+ import { binaryEx, expressionToString, functionInvocationEx, isQualifiedName, isPrimitive, listEx, memberEx, nullEx, numberEx, stringEx, trueEx, variableReferenceEx, } from '../ast/expressions.js';
4
+ import { AssignStatement, FunctionInvocationStatement, IfStatement, ForStatement, LabelledStatement, ParallelForStatement, ParallelStatement, RaiseStatement, ForRangeStatement, ReturnStatement, TryStatement, WhileStatement, DoWhileStatement, BreakStatement, ContinueStatement, SwitchStatement, } from '../ast/statements.js';
5
+ import { WorkflowSyntaxError } from '../errors.js';
6
+ import { convertExpression, convertMemberExpression, convertObjectExpression, isIntrinsic, throwIfSpread, isIntrinsicStatement, convertAssignmentTarget, } from './parseexpressions.js';
7
+ import { blockingFunctions } from './generated/functionMetadata.js';
8
+ export function parseStatement(node, ctx) {
9
+ switch (node.type) {
10
+ case AST_NODE_TYPES.BlockStatement:
11
+ return node.body.flatMap((node) => parseStatement(node, ctx));
12
+ case AST_NODE_TYPES.VariableDeclaration:
13
+ return convertVariableDeclarations(node, ctx);
14
+ case AST_NODE_TYPES.ExpressionStatement:
15
+ if (node.expression.type === AST_NODE_TYPES.AssignmentExpression) {
16
+ return assignmentExpressionToStatement(node.expression, ctx);
17
+ }
18
+ else if (node.expression.type === AST_NODE_TYPES.CallExpression) {
19
+ return callExpressionToStatement(node.expression, undefined, ctx);
20
+ }
21
+ else {
22
+ return [generalExpressionToAssignment(node.expression, ctx)];
23
+ }
24
+ case AST_NODE_TYPES.ReturnStatement:
25
+ return [createReturnStatement(node)];
26
+ case AST_NODE_TYPES.ThrowStatement:
27
+ return [createRaiseStatement(node)];
28
+ case AST_NODE_TYPES.IfStatement:
29
+ return [createIfStatement(node, ctx)];
30
+ case AST_NODE_TYPES.SwitchStatement:
31
+ return createSwitchStatement(node, ctx);
32
+ case AST_NODE_TYPES.ForStatement:
33
+ throw new WorkflowSyntaxError('for is not a supported construct. Use for...of.', node.loc);
34
+ case AST_NODE_TYPES.ForInStatement:
35
+ throw new WorkflowSyntaxError('for...in is not a supported construct. Use for...of.', node.loc);
36
+ case AST_NODE_TYPES.ForOfStatement:
37
+ return [createForOfStatement(node, ctx)];
38
+ case AST_NODE_TYPES.DoWhileStatement:
39
+ return createDoWhileStatement(node, ctx);
40
+ case AST_NODE_TYPES.WhileStatement:
41
+ return createWhileStatement(node, ctx);
42
+ case AST_NODE_TYPES.BreakStatement:
43
+ return [createBreakStatement(node)];
44
+ case AST_NODE_TYPES.ContinueStatement:
45
+ return [createContinueStatement(node)];
46
+ case AST_NODE_TYPES.TryStatement:
47
+ return createTryStatement(node, ctx);
48
+ case AST_NODE_TYPES.LabeledStatement:
49
+ return [createLabeledStatement(node, ctx)];
50
+ case AST_NODE_TYPES.EmptyStatement:
51
+ return [];
52
+ case AST_NODE_TYPES.FunctionDeclaration:
53
+ throw new WorkflowSyntaxError('Functions must be defined at the top level of a source file', node.loc);
54
+ case AST_NODE_TYPES.DebuggerStatement:
55
+ case AST_NODE_TYPES.TSInterfaceDeclaration:
56
+ case AST_NODE_TYPES.TSTypeAliasDeclaration:
57
+ case AST_NODE_TYPES.TSDeclareFunction:
58
+ // Ignore "debugger", "type", "interface" and "declare function"
59
+ return [];
60
+ default:
61
+ throw new WorkflowSyntaxError(`${node.type} is not supported`, node.loc);
62
+ }
63
+ }
64
+ function convertVariableDeclarations(node, ctx) {
65
+ if (node.kind !== 'const' && node.kind !== 'let') {
66
+ throw new WorkflowSyntaxError('Only const and let variable declarations are supported', node.loc);
67
+ }
68
+ return node.declarations.flatMap((decl) => {
69
+ if (decl.id.type === AST_NODE_TYPES.ArrayPattern) {
70
+ return convertArrayDestructuring(decl.id, decl.init, ctx);
71
+ }
72
+ else if (decl.id.type === AST_NODE_TYPES.ObjectPattern) {
73
+ return convertObjectDestructuring(decl.id, decl.init, ctx);
74
+ }
75
+ else {
76
+ // decl.id.type === AST_NODE_TYPES.Identifier
77
+ return convertInitializer(decl.id.name, decl.init, ctx);
78
+ }
79
+ });
80
+ }
81
+ function convertInitializer(targetVariableName, initializer, ctx) {
82
+ if (initializer?.type === AST_NODE_TYPES.CallExpression) {
83
+ const calleeName = initializer.callee.type === AST_NODE_TYPES.Identifier
84
+ ? initializer.callee.name
85
+ : undefined;
86
+ if (calleeName && isIntrinsicStatement(calleeName)) {
87
+ throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, initializer.callee.loc);
88
+ }
89
+ return callExpressionToStatement(initializer, targetVariableName, ctx);
90
+ }
91
+ else {
92
+ const name = variableReferenceEx(targetVariableName);
93
+ const value = initializer === null ? nullEx : convertExpression(initializer);
94
+ return [new AssignStatement([{ name, value }])];
95
+ }
96
+ }
97
+ function convertArrayDestructuring(arrayPattern, initializer, ctx) {
98
+ let initExpression;
99
+ const statements = [];
100
+ if (initializer?.type === AST_NODE_TYPES.Identifier ||
101
+ initializer?.type === AST_NODE_TYPES.MemberExpression) {
102
+ // If the initializer is an Identifier or MemberExpression (array variable?),
103
+ // use it directly. This ensures that the recursive variables gets initialized
104
+ // in the correct order. For example:
105
+ // const arr = [1, 2]
106
+ // [arr[1], arr[0]] = arr
107
+ initExpression = convertExpression(initializer);
108
+ }
109
+ else {
110
+ // Otherwise, assign the expression to a temporary variable first.
111
+ const initName = tempName(ctx);
112
+ statements.push(...convertInitializer(initName, initializer, ctx));
113
+ initExpression = variableReferenceEx(initName);
114
+ }
115
+ statements.push(...arrayDestructuringStatements(arrayPattern.elements, initExpression, ctx));
116
+ return statements;
117
+ }
118
+ function arrayDestructuringStatements(patterns, initializerExpression, ctx) {
119
+ if (patterns.filter((p) => p !== null).length === 0) {
120
+ return [];
121
+ }
122
+ const __temp_len = variableReferenceEx(`${tempName(ctx)}_len`);
123
+ const initializeVariables = [
124
+ {
125
+ name: __temp_len,
126
+ value: functionInvocationEx('len', [initializerExpression]),
127
+ },
128
+ ];
129
+ const branches = R.reverse(patterns).flatMap((pat, i) => {
130
+ if (pat === null) {
131
+ return [];
132
+ }
133
+ else {
134
+ return [
135
+ {
136
+ condition: binaryEx(__temp_len, '>=', numberEx(patterns.length - i)),
137
+ body: arrayElementsDestructuringStatements(patterns, initializerExpression, patterns.length - i, ctx),
138
+ },
139
+ ];
140
+ }
141
+ });
142
+ branches.push({
143
+ condition: trueEx,
144
+ body: arrayElementsDestructuringStatements(patterns, initializerExpression, 0, ctx),
145
+ });
146
+ return [new AssignStatement(initializeVariables), new IfStatement(branches)];
147
+ }
148
+ function arrayElementsDestructuringStatements(patterns, initializerExpression, take, ctx) {
149
+ return patterns.flatMap((pat, i) => {
150
+ if (i >= take) {
151
+ return [
152
+ new AssignStatement(extractDefaultAssignmentsFromDestructuringPattern(pat)),
153
+ ];
154
+ }
155
+ const iElement = memberEx(initializerExpression, numberEx(i), true);
156
+ switch (pat?.type) {
157
+ case AST_NODE_TYPES.MemberExpression:
158
+ case AST_NODE_TYPES.Identifier:
159
+ return [
160
+ new AssignStatement([
161
+ { name: convertAssignmentTarget(pat), value: iElement },
162
+ ]),
163
+ ];
164
+ case AST_NODE_TYPES.AssignmentPattern: {
165
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
166
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
167
+ }
168
+ const name = variableReferenceEx(pat.left.name);
169
+ return [new AssignStatement([{ name, value: iElement }])];
170
+ }
171
+ case AST_NODE_TYPES.ObjectPattern:
172
+ return objectDestructuringStatements(pat.properties, iElement, ctx);
173
+ case AST_NODE_TYPES.ArrayPattern:
174
+ return arrayDestructuringStatements(pat.elements, iElement, ctx);
175
+ case AST_NODE_TYPES.RestElement:
176
+ return arrayRestDestructuringStatements(patterns, pat, initializerExpression, patterns.length - 1, ctx);
177
+ default: // pat === null
178
+ return [];
179
+ }
180
+ });
181
+ }
182
+ function extractDefaultAssignmentsFromDestructuringPattern(pat) {
183
+ if (pat === null) {
184
+ return [];
185
+ }
186
+ switch (pat.type) {
187
+ case AST_NODE_TYPES.ArrayPattern:
188
+ return pat.elements.flatMap(extractDefaultAssignmentsFromDestructuringPattern);
189
+ case AST_NODE_TYPES.AssignmentPattern:
190
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
191
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
192
+ }
193
+ return [
194
+ {
195
+ name: variableReferenceEx(pat.left.name),
196
+ value: convertExpression(pat.right),
197
+ },
198
+ ];
199
+ case AST_NODE_TYPES.Identifier:
200
+ return [{ name: variableReferenceEx(pat.name), value: nullEx }];
201
+ case AST_NODE_TYPES.MemberExpression:
202
+ return [{ name: convertAssignmentTarget(pat), value: nullEx }];
203
+ case AST_NODE_TYPES.ObjectPattern:
204
+ return pat.properties.flatMap((p) => {
205
+ if (p.type === AST_NODE_TYPES.RestElement) {
206
+ if (p.argument.type !== AST_NODE_TYPES.Identifier) {
207
+ throw new WorkflowSyntaxError('Identifier expected', p.argument.loc);
208
+ }
209
+ return [{ name: variableReferenceEx(p.argument.name), value: nullEx }];
210
+ }
211
+ else if (p.value.type === AST_NODE_TYPES.ArrayPattern ||
212
+ p.value.type === AST_NODE_TYPES.AssignmentPattern ||
213
+ p.value.type === AST_NODE_TYPES.Identifier ||
214
+ p.value.type === AST_NODE_TYPES.MemberExpression ||
215
+ p.value.type === AST_NODE_TYPES.ObjectPattern) {
216
+ return extractDefaultAssignmentsFromDestructuringPattern(p.value);
217
+ }
218
+ else {
219
+ throw new WorkflowSyntaxError('Destructuring pattern expected', p.value.loc);
220
+ }
221
+ });
222
+ case AST_NODE_TYPES.RestElement:
223
+ if (pat.argument.type !== AST_NODE_TYPES.Identifier) {
224
+ throw new WorkflowSyntaxError('Identifier expected', pat.argument.loc);
225
+ }
226
+ return [
227
+ {
228
+ name: variableReferenceEx(pat.argument.name),
229
+ value: listEx([]),
230
+ },
231
+ ];
232
+ }
233
+ }
234
+ function throwIfInvalidRestElement(patterns) {
235
+ const i = patterns.findIndex((p) => p?.type === AST_NODE_TYPES.RestElement);
236
+ if (i >= 0 && i !== patterns.length - 1) {
237
+ throw new WorkflowSyntaxError('A rest element must be last in a destructuring pattern', patterns[i].loc);
238
+ }
239
+ }
240
+ function arrayRestDestructuringStatements(patterns, rest, initializerExpression, startIndex, ctx) {
241
+ throwIfInvalidRestElement(patterns);
242
+ if (rest.argument.type !== AST_NODE_TYPES.Identifier) {
243
+ throw new WorkflowSyntaxError('Identifier expected', rest.argument.loc);
244
+ }
245
+ const restName = variableReferenceEx(rest.argument.name);
246
+ const __temp_len = variableReferenceEx(`${tempName(ctx)}_len`);
247
+ const __temp_index = `${tempName(ctx)}_index`;
248
+ const one = numberEx(1);
249
+ const emptyArray = listEx([]);
250
+ const copyLoop = new ForRangeStatement([
251
+ new AssignStatement([
252
+ {
253
+ name: restName,
254
+ value: functionInvocationEx('list.concat', [
255
+ restName,
256
+ memberEx(initializerExpression, variableReferenceEx(__temp_index), true),
257
+ ]),
258
+ },
259
+ ]),
260
+ ], __temp_index, startIndex, binaryEx(__temp_len, '-', one));
261
+ return [
262
+ new AssignStatement([{ name: restName, value: emptyArray }]),
263
+ copyLoop,
264
+ ];
265
+ }
266
+ function convertObjectDestructuring(objectPattern, initializer, ctx) {
267
+ let initExpression;
268
+ const statements = [];
269
+ if (initializer?.type === AST_NODE_TYPES.Identifier ||
270
+ (initializer?.type === AST_NODE_TYPES.MemberExpression &&
271
+ isQualifiedName(convertExpression(initializer)))) {
272
+ // If the initializer is an Identifier or MemberExpression (object variable?), use it directly.
273
+ initExpression = convertExpression(initializer);
274
+ }
275
+ else {
276
+ // Otherwise, assign the expression to a temporary variable first.
277
+ const initName = tempName(ctx);
278
+ statements.push(...convertInitializer(initName, initializer, ctx));
279
+ initExpression = variableReferenceEx(initName);
280
+ }
281
+ statements.push(...objectDestructuringStatements(objectPattern.properties, initExpression, ctx));
282
+ return statements;
283
+ }
284
+ function objectDestructuringStatements(properties, initializerExpression, ctx) {
285
+ return properties.flatMap((prop) => {
286
+ if (prop.type === AST_NODE_TYPES.RestElement) {
287
+ return objectDestructuringRestStatements(properties, prop, initializerExpression);
288
+ }
289
+ if (prop.key.type !== AST_NODE_TYPES.Identifier) {
290
+ throw new WorkflowSyntaxError('Identifier expected', prop.key.loc);
291
+ }
292
+ const keyExpression = memberEx(initializerExpression, variableReferenceEx(prop.key.name), false);
293
+ if (prop.value.type === AST_NODE_TYPES.ObjectPattern) {
294
+ return objectDestructuringStatements(prop.value.properties, keyExpression, ctx);
295
+ }
296
+ else if (prop.value.type === AST_NODE_TYPES.ArrayPattern) {
297
+ return arrayDestructuringStatements(prop.value.elements, keyExpression, ctx);
298
+ }
299
+ else if (prop.value.type === AST_NODE_TYPES.Identifier) {
300
+ const safeKeyExpression = functionInvocationEx('map.get', [
301
+ initializerExpression,
302
+ stringEx(prop.key.name),
303
+ ]);
304
+ return [
305
+ new AssignStatement([
306
+ {
307
+ name: variableReferenceEx(prop.value.name),
308
+ value: safeKeyExpression,
309
+ },
310
+ ]),
311
+ ];
312
+ }
313
+ else if (prop.value.type === AST_NODE_TYPES.AssignmentPattern) {
314
+ return objectAssignmentPatternStatements(prop.value, initializerExpression, keyExpression);
315
+ }
316
+ else {
317
+ throw new WorkflowSyntaxError(`${prop.value.type} is not allowed in object destructuring`, prop.value.loc);
318
+ }
319
+ });
320
+ }
321
+ function objectAssignmentPatternStatements(pat, initializerExpression, keyExpression) {
322
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
323
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
324
+ }
325
+ // Using an if statement instead of default() because pat.right must be
326
+ // evaluated only in the default value branch (in case it has side effects)
327
+ const name = variableReferenceEx(pat.left.name);
328
+ return [
329
+ new IfStatement([
330
+ {
331
+ condition: binaryEx(stringEx(pat.left.name), 'in', initializerExpression),
332
+ body: [new AssignStatement([{ name, value: keyExpression }])],
333
+ },
334
+ {
335
+ condition: trueEx,
336
+ body: [
337
+ new AssignStatement([{ name, value: convertExpression(pat.right) }]),
338
+ ],
339
+ },
340
+ ]),
341
+ ];
342
+ }
343
+ function objectDestructuringRestStatements(properties, rest, initializerExpression) {
344
+ throwIfInvalidRestElement(properties);
345
+ if (rest.argument.type !== AST_NODE_TYPES.Identifier) {
346
+ throw new WorkflowSyntaxError('Identifier expected', rest.argument.loc);
347
+ }
348
+ const nonRestProperties = properties.filter((x) => x.type !== AST_NODE_TYPES.RestElement);
349
+ const nonRestKeys = nonRestProperties
350
+ .map((p) => p.key)
351
+ .map((k) => {
352
+ if (k.type !== AST_NODE_TYPES.Identifier) {
353
+ throw new WorkflowSyntaxError('Identifier expected', k.loc);
354
+ }
355
+ return k.name;
356
+ });
357
+ const name = variableReferenceEx(rest.argument.name);
358
+ const value = nonRestKeys.reduce((acc, propertyName) =>
359
+ // map.delete returns a copy of the object and removes the specified property
360
+ functionInvocationEx('map.delete', [acc, stringEx(propertyName)]), initializerExpression);
361
+ return [new AssignStatement([{ name, value }])];
362
+ }
363
+ function assignmentExpressionToStatement(node, ctx) {
364
+ let compoundOperator = undefined;
365
+ switch (node.operator) {
366
+ case '=':
367
+ compoundOperator = undefined;
368
+ break;
369
+ case '+=':
370
+ compoundOperator = '+';
371
+ break;
372
+ case '-=':
373
+ compoundOperator = '-';
374
+ break;
375
+ case '*=':
376
+ compoundOperator = '*';
377
+ break;
378
+ case '/=':
379
+ compoundOperator = '/';
380
+ break;
381
+ case '%=':
382
+ compoundOperator = '%';
383
+ break;
384
+ case '&&=':
385
+ compoundOperator = 'and';
386
+ break;
387
+ case '||=':
388
+ compoundOperator = 'or';
389
+ break;
390
+ default:
391
+ throw new WorkflowSyntaxError(`Operator ${node.operator} is not supported in assignment expressions`, node.loc);
392
+ }
393
+ if (compoundOperator === undefined) {
394
+ return assignmentStatements(node.left, node.right, ctx);
395
+ }
396
+ else {
397
+ return compoundAssignmentStatements(node.left, node.right, compoundOperator, ctx);
398
+ }
399
+ }
400
+ function assignmentStatements(left, right, ctx) {
401
+ let valueExpression;
402
+ const statements = [];
403
+ if (left.type === AST_NODE_TYPES.ArrayPattern) {
404
+ return convertArrayDestructuring(left, right, ctx);
405
+ }
406
+ else if (left.type === AST_NODE_TYPES.ObjectPattern) {
407
+ return convertObjectDestructuring(left, right, ctx);
408
+ }
409
+ if (right.type === AST_NODE_TYPES.CallExpression &&
410
+ right.callee.type === AST_NODE_TYPES.Identifier &&
411
+ isIntrinsic(right.callee.name)) {
412
+ const tr = convertAssignmentExpressionIntrinsicRHS(right, right.callee.name, ctx);
413
+ statements.push(...tr.statements);
414
+ valueExpression = tr.tempVariable;
415
+ }
416
+ else {
417
+ valueExpression = convertExpression(right);
418
+ }
419
+ const targetExpression = convertAssignmentTarget(left);
420
+ statements.push(new AssignStatement([{ name: targetExpression, value: valueExpression }]));
421
+ return statements;
422
+ }
423
+ function compoundAssignmentStatements(left, right, operator, ctx) {
424
+ let valueExpression;
425
+ const { expression: targetExpression, statements } = convertCompoundAssignmentLeftHandSide(left, ctx);
426
+ if (right.type === AST_NODE_TYPES.CallExpression &&
427
+ right.callee.type === AST_NODE_TYPES.Identifier &&
428
+ isIntrinsic(right.callee.name)) {
429
+ const tr = convertAssignmentExpressionIntrinsicRHS(right, right.callee.name, ctx);
430
+ statements.push(...tr.statements);
431
+ valueExpression = tr.tempVariable;
432
+ }
433
+ else {
434
+ valueExpression = convertExpression(right);
435
+ }
436
+ valueExpression = binaryEx(targetExpression, operator, valueExpression);
437
+ statements.push(new AssignStatement([{ name: targetExpression, value: valueExpression }]));
438
+ return statements;
439
+ }
440
+ function convertCompoundAssignmentLeftHandSide(left, ctx) {
441
+ const leftEx = convertAssignmentTarget(left);
442
+ const { transformed, assignments } = extractSideEffectsFromMemberExpression(leftEx, tempName(ctx), 0);
443
+ return {
444
+ expression: transformed,
445
+ statements: assignments.length > 0 ? [new AssignStatement(assignments)] : [],
446
+ };
447
+ }
448
+ /**
449
+ * Extract side-effecting computed properties into temporary variable assignments.
450
+ *
451
+ * This is used on the left-hand side of a compound assignment expression, which
452
+ * should only be evaluted once.
453
+ */
454
+ function extractSideEffectsFromMemberExpression(ex, tempPrefix, tempIndex) {
455
+ if (ex.tag === 'member' && ex.computed && !isPrimitive(ex.property)) {
456
+ // property potentially has side effects. Move to a temp variable for safety.
457
+ let transformedObject;
458
+ let objectAssignments;
459
+ if (ex.object.tag === 'member') {
460
+ const object2 = extractSideEffectsFromMemberExpression(ex.object, tempPrefix, tempIndex + 1);
461
+ transformedObject = object2.transformed;
462
+ objectAssignments = object2.assignments;
463
+ }
464
+ else {
465
+ transformedObject = ex.object;
466
+ objectAssignments = [];
467
+ }
468
+ const tmp = variableReferenceEx(`${tempPrefix}${tempIndex}`);
469
+ const transformed = memberEx(transformedObject, tmp, true);
470
+ const assignments = objectAssignments;
471
+ assignments.push({
472
+ name: tmp,
473
+ value: ex.property,
474
+ });
475
+ return { transformed, assignments };
476
+ }
477
+ else if (ex.tag === 'member' && ex.object.tag === 'member') {
478
+ const { transformed: object2, assignments } = extractSideEffectsFromMemberExpression(ex.object, tempPrefix, tempIndex);
479
+ const transformed = memberEx(object2, ex.property, ex.computed);
480
+ return { transformed, assignments };
481
+ }
482
+ else {
483
+ return {
484
+ transformed: ex,
485
+ assignments: [],
486
+ };
487
+ }
488
+ }
489
+ /**
490
+ * Special case for handling call_step() RHS in assignment expressions.
491
+ */
492
+ function convertAssignmentExpressionIntrinsicRHS(callEx, calleeName, ctx) {
493
+ if (isIntrinsicStatement(calleeName)) {
494
+ throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, callEx.callee.loc);
495
+ }
496
+ const resultVariable = tempName(ctx);
497
+ return {
498
+ statements: callExpressionToStatement(callEx, resultVariable, ctx),
499
+ tempVariable: variableReferenceEx(resultVariable),
500
+ };
501
+ }
502
+ function callExpressionToStatement(node, resultVariable, ctx) {
503
+ const calleeExpression = convertExpression(node.callee);
504
+ if (isQualifiedName(calleeExpression)) {
505
+ const calleeName = expressionToString(calleeExpression);
506
+ if (calleeName === 'parallel') {
507
+ // A handle the "parallel" intrinsic
508
+ return [createParallelStatement(node.arguments, node.loc, ctx)];
509
+ }
510
+ else if (calleeName === 'retry_policy') {
511
+ // retry_policy() is handled by AST_NODE_TYPES.TryStatement and therefore ignored here
512
+ return [];
513
+ }
514
+ else if (calleeName === 'call_step') {
515
+ return [createCallStatement(node, node.arguments, resultVariable)];
516
+ }
517
+ else if (blockingFunctions.has(calleeName)) {
518
+ const argumentNames = blockingFunctions.get(calleeName) ?? [];
519
+ return [
520
+ blockingFunctionStatement(calleeName, argumentNames, node.arguments, resultVariable),
521
+ ];
522
+ }
523
+ else {
524
+ const resultVariable2 = resultVariable ?? tempName(ctx);
525
+ return [
526
+ callExpressionAssignment(calleeName, node.arguments, resultVariable2),
527
+ ];
528
+ }
529
+ }
530
+ else {
531
+ throw new WorkflowSyntaxError('Expeced a subworkflow or a standard library function name', node.callee.loc);
532
+ }
533
+ }
534
+ function callExpressionAssignment(functionName, argumentsNode, resultVariable) {
535
+ const argumentExpressions = throwIfSpread(argumentsNode).map(convertExpression);
536
+ return new AssignStatement([
537
+ {
538
+ name: variableReferenceEx(resultVariable),
539
+ value: functionInvocationEx(functionName, argumentExpressions),
540
+ },
541
+ ]);
542
+ }
543
+ function createCallStatement(node, argumentsNode, resultVariable) {
544
+ if (argumentsNode.length < 1) {
545
+ throw new WorkflowSyntaxError('The first argument must be a Function', node.loc);
546
+ }
547
+ let functionName;
548
+ const argNode = argumentsNode[0].type === AST_NODE_TYPES.TSInstantiationExpression
549
+ ? argumentsNode[0].expression
550
+ : argumentsNode[0];
551
+ if (argNode.type === AST_NODE_TYPES.Identifier) {
552
+ functionName = argNode.name;
553
+ }
554
+ else if (argNode.type === AST_NODE_TYPES.MemberExpression) {
555
+ const memberExp = convertMemberExpression(argNode);
556
+ if (!isQualifiedName(memberExp)) {
557
+ throw new WorkflowSyntaxError('Function name must be a qualified name', argNode.loc);
558
+ }
559
+ functionName = expressionToString(memberExp);
560
+ }
561
+ else {
562
+ throw new WorkflowSyntaxError('Expected an identifier or a member expression', argNode.loc);
563
+ }
564
+ let args = {};
565
+ if (argumentsNode.length >= 2) {
566
+ if (argumentsNode[1].type !== AST_NODE_TYPES.ObjectExpression) {
567
+ throw new WorkflowSyntaxError('The second argument must be a map literal', argumentsNode[1].loc);
568
+ }
569
+ args = convertObjectExpression(argumentsNode[1]).value;
570
+ }
571
+ return new FunctionInvocationStatement(functionName, args, resultVariable);
572
+ }
573
+ function blockingFunctionStatement(functionName, argumentNames, argumentsNode, resultName) {
574
+ const argumentExpressions = throwIfSpread(argumentsNode).map(convertExpression);
575
+ const args = Object.fromEntries(argumentNames.flatMap((argName, i) => {
576
+ if (i >= argumentExpressions.length) {
577
+ return [];
578
+ }
579
+ else if (argumentsNode[i].type === AST_NODE_TYPES.Identifier &&
580
+ argumentsNode[i].name === 'undefined') {
581
+ return [];
582
+ }
583
+ else {
584
+ return [[argName, argumentExpressions[i]]];
585
+ }
586
+ }));
587
+ return new FunctionInvocationStatement(functionName, args, resultName);
588
+ }
589
+ function createParallelStatement(args, loc, ctx) {
590
+ const { shared, concurrencyLimit, exceptionPolicy } = parseParallelOptions(args[1]);
591
+ const ctx2 = Object.assign({}, ctx, {
592
+ parallelNestingLevel: ctx.parallelNestingLevel
593
+ ? ctx.parallelNestingLevel + 1
594
+ : 1,
595
+ });
596
+ switch (args[0]?.type) {
597
+ case AST_NODE_TYPES.ArrayExpression:
598
+ return new ParallelStatement(parseParallelBranches(args[0], ctx2), shared, concurrencyLimit, exceptionPolicy);
599
+ case AST_NODE_TYPES.ArrowFunctionExpression:
600
+ return new ParallelForStatement(parseParallelIteration(args[0], ctx2), shared, concurrencyLimit, exceptionPolicy);
601
+ case undefined:
602
+ throw new WorkflowSyntaxError('At least one argument required', loc);
603
+ default:
604
+ throw new WorkflowSyntaxError('The first parameter must be an array of functions or an arrow function', args[0].loc);
605
+ }
606
+ }
607
+ function parseParallelBranches(node, ctx) {
608
+ const branches = node.elements.map((arg) => {
609
+ switch (arg?.type) {
610
+ case AST_NODE_TYPES.Identifier:
611
+ return [new FunctionInvocationStatement(arg.name)];
612
+ case AST_NODE_TYPES.ArrowFunctionExpression:
613
+ if (arg.body.type !== AST_NODE_TYPES.BlockStatement) {
614
+ throw new WorkflowSyntaxError('The body must be a block statement', arg.body.loc);
615
+ }
616
+ if (arg.params.length > 0) {
617
+ throw new WorkflowSyntaxError('Parallel functions must not take arguments', arg.params[0].loc);
618
+ }
619
+ return parseStatement(arg.body, ctx);
620
+ default:
621
+ throw new WorkflowSyntaxError('Argument should be a function of type () => void', arg ? arg.loc : node.loc);
622
+ }
623
+ });
624
+ return branches.map((statements, i) => ({
625
+ name: `branch${i + 1}`,
626
+ body: statements,
627
+ }));
628
+ }
629
+ function parseParallelIteration(node, ctx) {
630
+ if (node.body.type !== AST_NODE_TYPES.BlockStatement ||
631
+ node.body.body.length !== 1 ||
632
+ node.body.body[0].type !== AST_NODE_TYPES.ForOfStatement) {
633
+ throw new WorkflowSyntaxError('The parallel function body must be a single for...of statement', node.body.loc);
634
+ }
635
+ return createForOfStatement(node.body.body[0], ctx);
636
+ }
637
+ function parseParallelOptions(node) {
638
+ if (node === undefined) {
639
+ return {};
640
+ }
641
+ if (node.type !== AST_NODE_TYPES.ObjectExpression) {
642
+ throw new WorkflowSyntaxError('The second parameter must be an object', node.loc);
643
+ }
644
+ const parallelOptions = convertObjectExpression(node);
645
+ const opts = parallelOptions.value;
646
+ const sharedExpression = opts.shared;
647
+ if (sharedExpression && sharedExpression.tag !== 'list') {
648
+ throw new WorkflowSyntaxError('"shared" must be an array of strings', node.loc);
649
+ }
650
+ const shared = sharedExpression?.value.map((x) => {
651
+ if (x.tag !== 'string') {
652
+ throw new WorkflowSyntaxError('"shared" must be an array of strings', node.loc);
653
+ }
654
+ return x;
655
+ });
656
+ const concurrencyLimitExpression = opts.concurrency_limit;
657
+ if (concurrencyLimitExpression &&
658
+ concurrencyLimitExpression.tag !== 'number') {
659
+ throw new WorkflowSyntaxError('"concurrency_limit" must be a number', node.loc);
660
+ }
661
+ const exceptionPolicyExpression = opts.exception_policy;
662
+ if (exceptionPolicyExpression && exceptionPolicyExpression.tag !== 'string') {
663
+ throw new WorkflowSyntaxError('"exception_policy" must be a string', node.loc);
664
+ }
665
+ return {
666
+ shared: shared?.map((x) => x.value),
667
+ concurrencyLimit: concurrencyLimitExpression?.value,
668
+ exceptionPolicy: exceptionPolicyExpression?.value,
669
+ };
670
+ }
671
+ function generalExpressionToAssignment(node, ctx) {
672
+ return new AssignStatement([
673
+ {
674
+ name: variableReferenceEx(tempName(ctx)),
675
+ value: convertExpression(node),
676
+ },
677
+ ]);
678
+ }
679
+ function createReturnStatement(node) {
680
+ const value = node.argument ? convertExpression(node.argument) : undefined;
681
+ return new ReturnStatement(value);
682
+ }
683
+ function createRaiseStatement(node) {
684
+ return new RaiseStatement(convertExpression(node.argument));
685
+ }
686
+ function createIfStatement(node, ctx) {
687
+ return new IfStatement(flattenIfBranches(node, ctx));
688
+ }
689
+ function flattenIfBranches(ifStatement, ctx) {
690
+ const branches = [
691
+ {
692
+ condition: convertExpression(ifStatement.test),
693
+ body: parseStatement(ifStatement.consequent, ctx),
694
+ },
695
+ ];
696
+ if (ifStatement.alternate) {
697
+ if (ifStatement.alternate.type === AST_NODE_TYPES.IfStatement) {
698
+ branches.push(...flattenIfBranches(ifStatement.alternate, ctx));
699
+ }
700
+ else {
701
+ branches.push({
702
+ condition: trueEx,
703
+ body: parseStatement(ifStatement.alternate, ctx),
704
+ });
705
+ }
706
+ }
707
+ return branches;
708
+ }
709
+ function createSwitchStatement(node, ctx) {
710
+ const discriminant = convertExpression(node.discriminant);
711
+ const branches = node.cases.map((switchCase) => {
712
+ let condition;
713
+ if (switchCase.test) {
714
+ const test = convertExpression(switchCase.test);
715
+ condition = binaryEx(discriminant, '==', test);
716
+ }
717
+ else {
718
+ condition = trueEx;
719
+ }
720
+ const body = switchCase.consequent.flatMap((x) => parseStatement(x, ctx));
721
+ return { condition, body };
722
+ });
723
+ return [new SwitchStatement(branches)];
724
+ }
725
+ function createForOfStatement(node, ctx) {
726
+ const bodyCtx = Object.assign({}, ctx, {
727
+ continueTarget: undefined,
728
+ breakTarget: undefined,
729
+ });
730
+ const statements = parseStatement(node.body, bodyCtx);
731
+ let loopVariableName;
732
+ if (node.left.type === AST_NODE_TYPES.Identifier) {
733
+ loopVariableName = node.left.name;
734
+ }
735
+ else if (node.left.type === AST_NODE_TYPES.VariableDeclaration &&
736
+ node.left.declarations.length >= 1) {
737
+ const declaration = node.left.declarations[0];
738
+ if (declaration.id.type !== AST_NODE_TYPES.Identifier) {
739
+ throw new WorkflowSyntaxError(`Identifier expected, got ${declaration.id.type}`, declaration.id.loc);
740
+ }
741
+ loopVariableName = declaration.id.name;
742
+ }
743
+ else {
744
+ throw new WorkflowSyntaxError('Unsupported initializer', node.left.loc);
745
+ }
746
+ const listExpression = convertExpression(node.right);
747
+ if (isPrimitive(listExpression) || listExpression.tag === 'map') {
748
+ throw new WorkflowSyntaxError('Must be a list expression', node.right.loc);
749
+ }
750
+ return new ForStatement(statements, loopVariableName, listExpression);
751
+ }
752
+ function createWhileStatement(node, ctx) {
753
+ const condition = convertExpression(node.test);
754
+ const body = parseStatement(node.body, ctx);
755
+ return [new WhileStatement(condition, body)];
756
+ }
757
+ function createDoWhileStatement(node, ctx) {
758
+ const body = parseStatement(node.body, ctx);
759
+ const condition = convertExpression(node.test);
760
+ return [new DoWhileStatement(condition, body)];
761
+ }
762
+ function createBreakStatement(node) {
763
+ return new BreakStatement(node.label?.name);
764
+ }
765
+ function createContinueStatement(node) {
766
+ return new ContinueStatement(node.label?.name);
767
+ }
768
+ function createTryStatement(node, ctx) {
769
+ const retryPolicy = extractRetryPolicy(node.block);
770
+ const tryBody = parseStatement(node.block, ctx);
771
+ let exceptBody = undefined;
772
+ let errorVariable = undefined;
773
+ if (node.handler) {
774
+ exceptBody = parseStatement(node.handler.body, ctx);
775
+ errorVariable = extractErrorVariableName(node.handler.param);
776
+ }
777
+ let finalizerBody = undefined;
778
+ if (node.finalizer) {
779
+ finalizerBody = parseStatement(node.finalizer, ctx);
780
+ }
781
+ return [
782
+ new TryStatement(tryBody, exceptBody, retryPolicy, errorVariable, finalizerBody),
783
+ ];
784
+ }
785
+ function extractRetryPolicy(tryBlock) {
786
+ // Find and parse the first retry_policy() in tryBlock
787
+ for (const statement of tryBlock.body) {
788
+ if (statement.type === AST_NODE_TYPES.ExpressionStatement &&
789
+ statement.expression.type === AST_NODE_TYPES.CallExpression &&
790
+ statement.expression.callee.type === AST_NODE_TYPES.Identifier &&
791
+ statement.expression.callee.name === 'retry_policy') {
792
+ if (statement.expression.arguments.length < 1) {
793
+ throw new WorkflowSyntaxError('Required argument missing', statement.expression.loc);
794
+ }
795
+ const arg0 = throwIfSpread(statement.expression.arguments).map(convertExpression)[0];
796
+ const argsLoc = statement.expression.arguments[0].loc;
797
+ if (isQualifiedName(arg0)) {
798
+ return expressionToString(arg0);
799
+ }
800
+ else if (arg0.tag === 'map') {
801
+ return retryPolicyFromParams(arg0.value, argsLoc);
802
+ }
803
+ else {
804
+ throw new WorkflowSyntaxError('Unexpected type', argsLoc);
805
+ }
806
+ }
807
+ }
808
+ return undefined;
809
+ }
810
+ function retryPolicyFromParams(params, argsLoc) {
811
+ if (!('backoff' in params)) {
812
+ throw new WorkflowSyntaxError('Required parameter "backoff" missing', argsLoc);
813
+ }
814
+ else if (params.backoff.tag !== 'map') {
815
+ throw new WorkflowSyntaxError('Expected "backoff" to be an object literal', argsLoc);
816
+ }
817
+ const backoff = params.backoff.value;
818
+ return {
819
+ predicate: predicateFromRetryParams(params, argsLoc),
820
+ maxRetries: params.max_retries,
821
+ backoff: {
822
+ initialDelay: backoff.initial_delay,
823
+ maxDelay: backoff.max_delay,
824
+ multiplier: backoff.multiplier,
825
+ },
826
+ };
827
+ }
828
+ function predicateFromRetryParams(params, argsLoc) {
829
+ if (!('predicate' in params)) {
830
+ return undefined;
831
+ }
832
+ else if (isQualifiedName(params.predicate)) {
833
+ return expressionToString(params.predicate);
834
+ }
835
+ else {
836
+ throw new WorkflowSyntaxError('"predicate" must be a function name', argsLoc);
837
+ }
838
+ }
839
+ function extractErrorVariableName(param) {
840
+ if (!param) {
841
+ return undefined;
842
+ }
843
+ if (param.type !== AST_NODE_TYPES.Identifier) {
844
+ throw new WorkflowSyntaxError('The error variable must be an identifier', param.loc);
845
+ }
846
+ return param.name;
847
+ }
848
+ function createLabeledStatement(node, ctx) {
849
+ return new LabelledStatement(node.label.name, parseStatement(node.body, ctx));
850
+ }
851
+ function tempName(ctx) {
852
+ if (ctx.parallelNestingLevel !== undefined) {
853
+ // Temporary variable inside a parallel step can not be the same as temporary
854
+ // variables on the outside. Sharing the variable name would cause deployment
855
+ // error, if the variable is not marked as shared by including it in the
856
+ // "shared" array.
857
+ return `__temp_parallel${ctx.parallelNestingLevel}`;
858
+ }
859
+ else {
860
+ return '__temp';
861
+ }
862
+ }