ts2workflows 0.8.0 → 0.10.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.
@@ -1,19 +1,18 @@
1
+ import * as R from 'ramda';
1
2
  import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
2
- import { AssignStepAST, CallStepAST, ForStepAST, JumpTargetAST, NextStepAST, ParallelStepAST, RaiseStepAST, ReturnStepAST, StepsStepAST, SwitchStepAST, TryStepAST, } from '../ast/steps.js';
3
- import { BinaryExpression, FunctionInvocationExpression, PrimitiveExpression, VariableReferenceExpression, isExpression, isFullyQualifiedName, isLiteral, } from '../ast/expressions.js';
3
+ import { AssignStepAST, CallStepAST, ForRangeStepAST, ForStepAST, JumpTargetAST, NextStepAST, ParallelStepAST, RaiseStepAST, ReturnStepAST, StepsStepAST, SwitchStepAST, TryStepAST, } from '../ast/steps.js';
4
+ import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, VariableReferenceExpression, asExpression, isExpression, isFullyQualifiedName, isLiteral, isPure, nullEx, safeAsExpression, trueEx, } from '../ast/expressions.js';
4
5
  import { InternalTranspilingError, WorkflowSyntaxError } from '../errors.js';
5
- import { flatMapPair, isRecord, mapRecordValues } from '../utils.js';
6
- import { transformAST } from './transformations.js';
7
- import { convertExpression, convertMemberExpression, convertObjectExpression, convertObjectAsExpressionValues, isMagicFunction, throwIfSpread, isMagicFunctionStatmentOnly, asExpression, } from './expressions.js';
6
+ import { chainPairs, isRecord } from '../utils.js';
7
+ import { convertExpression, convertMemberExpression, convertObjectExpression, convertObjectAsExpressionValues, isIntrinsic, throwIfSpread, isIntrinsicStatment as isIntrinsicStatement, convertVariableNameExpression, } from './expressions.js';
8
8
  import { blockingFunctions } from './generated/functionMetadata.js';
9
- export function parseStatement(node, ctx, postSteps) {
10
- const steps = parseStatementRecursively(node, undefined, ctx);
11
- return transformAST(steps.concat(postSteps ?? []));
9
+ export function parseStatement(node, ctx) {
10
+ return parseStatementRecursively(node, undefined, ctx);
12
11
  }
13
12
  function parseStatementRecursively(node, nextNode, ctx) {
14
13
  switch (node.type) {
15
14
  case AST_NODE_TYPES.BlockStatement:
16
- return flatMapPair(node.body, (statement, nextStatement) => parseStatementRecursively(statement, nextStatement, ctx));
15
+ return chainPairs(R.partialRight(parseStatementRecursively, [ctx]), node.body);
17
16
  case AST_NODE_TYPES.VariableDeclaration:
18
17
  return convertVariableDeclarations(node.declarations, ctx);
19
18
  case AST_NODE_TYPES.ExpressionStatement:
@@ -24,7 +23,7 @@ function parseStatementRecursively(node, nextNode, ctx) {
24
23
  return callExpressionToStep(node.expression, undefined, ctx);
25
24
  }
26
25
  else {
27
- return [generalExpressionToAssignStep(node.expression)];
26
+ return [generalExpressionToAssignStep(node.expression, ctx)];
28
27
  }
29
28
  case AST_NODE_TYPES.ReturnStatement:
30
29
  return [returnStatementToReturnStep(node, ctx)];
@@ -71,27 +70,302 @@ function parseStatementRecursively(node, nextNode, ctx) {
71
70
  }
72
71
  function convertVariableDeclarations(declarations, ctx) {
73
72
  return declarations.flatMap((decl) => {
74
- if (decl.id.type !== AST_NODE_TYPES.Identifier) {
75
- throw new WorkflowSyntaxError('Expected Identifier', decl.loc);
76
- }
77
- const targetName = decl.id.name;
78
- if (decl.init?.type === AST_NODE_TYPES.CallExpression) {
79
- const calleeName = decl.init.callee.type === AST_NODE_TYPES.Identifier
80
- ? decl.init.callee.name
81
- : undefined;
82
- if (calleeName && isMagicFunctionStatmentOnly(calleeName)) {
83
- throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, decl.init.callee.loc);
73
+ if (decl.id.type === AST_NODE_TYPES.Identifier) {
74
+ return convertInitializer(decl.id.name, decl.init, ctx);
75
+ }
76
+ else if (decl.id.type === AST_NODE_TYPES.ArrayPattern) {
77
+ return convertArrayDestructuring(decl.id, decl.init, ctx);
78
+ }
79
+ else if (decl.id.type === AST_NODE_TYPES.ObjectPattern) {
80
+ return convertObjectDestructuring(decl.id, decl.init, ctx);
81
+ }
82
+ else {
83
+ throw new WorkflowSyntaxError('Unsupported pattern', decl.loc);
84
+ }
85
+ });
86
+ }
87
+ function convertInitializer(targetVariableName, initializer, ctx) {
88
+ if (initializer?.type === AST_NODE_TYPES.CallExpression) {
89
+ const calleeName = initializer.callee.type === AST_NODE_TYPES.Identifier
90
+ ? initializer.callee.name
91
+ : undefined;
92
+ if (calleeName && isIntrinsicStatement(calleeName)) {
93
+ throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, initializer.callee.loc);
94
+ }
95
+ return callExpressionToStep(initializer, targetVariableName, ctx);
96
+ }
97
+ else {
98
+ const name = new VariableReferenceExpression(targetVariableName);
99
+ const value = initializer === null ? nullEx : convertExpression(initializer);
100
+ return [new AssignStepAST([{ name, value }])];
101
+ }
102
+ }
103
+ function convertArrayDestructuring(arrayPattern, initializer, ctx) {
104
+ let initExpression;
105
+ const steps = [];
106
+ if (initializer?.type === AST_NODE_TYPES.Identifier ||
107
+ initializer?.type === AST_NODE_TYPES.MemberExpression) {
108
+ // If the initializer is an Identifier or MemberExpression (array variable?),
109
+ // use it directly. This ensures that the recursive variables gets initialized
110
+ // in the correct order. For example:
111
+ // const arr = [1, 2]
112
+ // [arr[1], arr[0]] = arr
113
+ initExpression = convertExpression(initializer);
114
+ }
115
+ else {
116
+ // Otherwise, assign the expression to a temporary variable first.
117
+ const initName = tempName(ctx);
118
+ steps.push(...convertInitializer(initName, initializer, ctx));
119
+ initExpression = new VariableReferenceExpression(initName);
120
+ }
121
+ steps.push(...arrayDestructuringSteps(arrayPattern.elements, initExpression, ctx));
122
+ return steps;
123
+ }
124
+ function arrayDestructuringSteps(patterns, initializerExpression, ctx) {
125
+ if (patterns.filter((p) => p !== null).length === 0) {
126
+ return [];
127
+ }
128
+ const __temp_len = new VariableReferenceExpression(`${tempName(ctx)}_len`);
129
+ const initializeVariables = [
130
+ {
131
+ name: __temp_len,
132
+ value: new FunctionInvocationExpression('len', [initializerExpression]),
133
+ },
134
+ ];
135
+ const branches = R.reverse(patterns).flatMap((pat, i) => {
136
+ if (pat === null) {
137
+ return [];
138
+ }
139
+ else {
140
+ return [
141
+ {
142
+ condition: new BinaryExpression(__temp_len, '>=', new PrimitiveExpression(patterns.length - i)),
143
+ steps: arrayElementsDestructuringSteps(patterns, initializerExpression, patterns.length - i, ctx),
144
+ },
145
+ ];
146
+ }
147
+ });
148
+ branches.push({
149
+ condition: trueEx,
150
+ steps: arrayElementsDestructuringSteps(patterns, initializerExpression, 0, ctx),
151
+ });
152
+ return [new AssignStepAST(initializeVariables), new SwitchStepAST(branches)];
153
+ }
154
+ function arrayElementsDestructuringSteps(patterns, initializerExpression, take, ctx) {
155
+ return patterns.flatMap((pat, i) => {
156
+ if (i >= take) {
157
+ return [
158
+ new AssignStepAST(extractDefaultAssignmentsFromDestructuringPattern(pat)),
159
+ ];
160
+ }
161
+ const iElement = new MemberExpression(initializerExpression, new PrimitiveExpression(i), true);
162
+ switch (pat?.type) {
163
+ case AST_NODE_TYPES.MemberExpression:
164
+ case AST_NODE_TYPES.Identifier: {
165
+ const name = convertVariableNameExpression(pat);
166
+ return [new AssignStepAST([{ name, value: iElement }])];
167
+ }
168
+ case AST_NODE_TYPES.AssignmentPattern: {
169
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
170
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
171
+ }
172
+ const name = new VariableReferenceExpression(pat.left.name);
173
+ return [new AssignStepAST([{ name, value: iElement }])];
174
+ }
175
+ case AST_NODE_TYPES.ObjectPattern:
176
+ return objectDestructuringSteps(pat.properties, iElement, ctx);
177
+ case AST_NODE_TYPES.ArrayPattern:
178
+ return arrayDestructuringSteps(pat.elements, iElement, ctx);
179
+ case AST_NODE_TYPES.RestElement:
180
+ return arrayRestDestructuringSteps(patterns, pat, initializerExpression, patterns.length - 1, ctx);
181
+ default: // pat === null
182
+ return [];
183
+ }
184
+ });
185
+ }
186
+ function extractDefaultAssignmentsFromDestructuringPattern(pat) {
187
+ if (pat === null) {
188
+ return [];
189
+ }
190
+ switch (pat.type) {
191
+ case AST_NODE_TYPES.ArrayPattern:
192
+ return pat.elements.flatMap(extractDefaultAssignmentsFromDestructuringPattern);
193
+ case AST_NODE_TYPES.AssignmentPattern:
194
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
195
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
84
196
  }
85
- return callExpressionToStep(decl.init, targetName, ctx);
197
+ return [
198
+ {
199
+ name: new VariableReferenceExpression(pat.left.name),
200
+ value: convertExpression(pat.right),
201
+ },
202
+ ];
203
+ case AST_NODE_TYPES.Identifier:
204
+ return [
205
+ { name: new VariableReferenceExpression(pat.name), value: nullEx },
206
+ ];
207
+ case AST_NODE_TYPES.MemberExpression:
208
+ return [{ name: convertVariableNameExpression(pat), value: nullEx }];
209
+ case AST_NODE_TYPES.ObjectPattern:
210
+ return pat.properties.flatMap((p) => {
211
+ if (p.type === AST_NODE_TYPES.RestElement) {
212
+ return extractDefaultAssignmentsFromDestructuringPattern(p);
213
+ }
214
+ else if (p.value.type === AST_NODE_TYPES.ArrayPattern ||
215
+ p.value.type === AST_NODE_TYPES.AssignmentPattern ||
216
+ p.value.type === AST_NODE_TYPES.Identifier ||
217
+ p.value.type === AST_NODE_TYPES.MemberExpression ||
218
+ p.value.type === AST_NODE_TYPES.ObjectPattern) {
219
+ return extractDefaultAssignmentsFromDestructuringPattern(p.value);
220
+ }
221
+ else {
222
+ throw new WorkflowSyntaxError('Destructuring pattern expected', p.value.loc);
223
+ }
224
+ });
225
+ case AST_NODE_TYPES.RestElement:
226
+ if (pat.argument.type !== AST_NODE_TYPES.Identifier) {
227
+ throw new WorkflowSyntaxError('Identifier expected', pat.argument.loc);
228
+ }
229
+ return [
230
+ {
231
+ name: new VariableReferenceExpression(pat.argument.name),
232
+ value: new PrimitiveExpression([]),
233
+ },
234
+ ];
235
+ default:
236
+ return [];
237
+ }
238
+ }
239
+ function throwIfInvalidRestElement(patterns) {
240
+ const i = patterns.findIndex((p) => p?.type === AST_NODE_TYPES.RestElement);
241
+ if (i >= 0 && i !== patterns.length - 1) {
242
+ throw new WorkflowSyntaxError('A rest element must be last in a destructuring pattern', patterns[i].loc);
243
+ }
244
+ }
245
+ function arrayRestDestructuringSteps(patterns, rest, initializerExpression, startIndex, ctx) {
246
+ throwIfInvalidRestElement(patterns);
247
+ if (rest.argument.type !== AST_NODE_TYPES.Identifier) {
248
+ throw new WorkflowSyntaxError('Identifier expected', rest.argument.loc);
249
+ }
250
+ const restName = new VariableReferenceExpression(rest.argument.name);
251
+ const __temp_len = new VariableReferenceExpression(`${tempName(ctx)}_len`);
252
+ const __temp_index = `${tempName(ctx)}_index`;
253
+ const one = new PrimitiveExpression(1);
254
+ const emptyArray = new PrimitiveExpression([]);
255
+ const copyLoop = new ForRangeStepAST([
256
+ new AssignStepAST([
257
+ {
258
+ name: restName,
259
+ value: new FunctionInvocationExpression('list.concat', [
260
+ restName,
261
+ new MemberExpression(initializerExpression, new VariableReferenceExpression(__temp_index), true),
262
+ ]),
263
+ },
264
+ ]),
265
+ ], __temp_index, startIndex, new BinaryExpression(__temp_len, '-', one));
266
+ return [new AssignStepAST([{ name: restName, value: emptyArray }]), copyLoop];
267
+ }
268
+ function convertObjectDestructuring(objectPattern, initializer, ctx) {
269
+ let initExpression;
270
+ const steps = [];
271
+ if (initializer?.type === AST_NODE_TYPES.Identifier ||
272
+ (initializer?.type === AST_NODE_TYPES.MemberExpression &&
273
+ isPure(convertExpression(initializer)))) {
274
+ // If the initializer is an Identifier or MemberExpression (object variable?), use it directly.
275
+ initExpression = convertExpression(initializer);
276
+ }
277
+ else {
278
+ // Otherwise, assign the expression to a temporary variable first.
279
+ const initName = tempName(ctx);
280
+ steps.push(...convertInitializer(initName, initializer, ctx));
281
+ initExpression = new VariableReferenceExpression(initName);
282
+ }
283
+ steps.push(...objectDestructuringSteps(objectPattern.properties, initExpression, ctx));
284
+ return steps;
285
+ }
286
+ function objectDestructuringSteps(properties, initializerExpression, ctx) {
287
+ return properties.flatMap((prop) => {
288
+ if (prop.type === AST_NODE_TYPES.RestElement) {
289
+ return objectDestructuringRestSteps(properties, prop, initializerExpression);
290
+ }
291
+ if (prop.key.type !== AST_NODE_TYPES.Identifier) {
292
+ throw new WorkflowSyntaxError('Identifier expected', prop.key.loc);
293
+ }
294
+ const keyExpression = new MemberExpression(initializerExpression, new VariableReferenceExpression(prop.key.name), false);
295
+ if (prop.value.type === AST_NODE_TYPES.ObjectPattern) {
296
+ return objectDestructuringSteps(prop.value.properties, keyExpression, ctx);
297
+ }
298
+ else if (prop.value.type === AST_NODE_TYPES.ArrayPattern) {
299
+ return arrayDestructuringSteps(prop.value.elements, keyExpression, ctx);
300
+ }
301
+ else if (prop.value.type === AST_NODE_TYPES.Identifier) {
302
+ const safeKeyExpression = new FunctionInvocationExpression('map.get', [
303
+ initializerExpression,
304
+ new PrimitiveExpression(prop.key.name),
305
+ ]);
306
+ return [
307
+ new AssignStepAST([
308
+ {
309
+ name: new VariableReferenceExpression(prop.value.name),
310
+ value: safeKeyExpression,
311
+ },
312
+ ]),
313
+ ];
314
+ }
315
+ else if (prop.value.type === AST_NODE_TYPES.AssignmentPattern) {
316
+ return objectAssignmentPatternSteps(prop.value, initializerExpression, keyExpression);
86
317
  }
87
318
  else {
88
- const value = decl.init == null
89
- ? new PrimitiveExpression(null)
90
- : convertExpression(decl.init);
91
- return [new AssignStepAST([[targetName, value]])];
319
+ throw new WorkflowSyntaxError(`${prop.value.type} is not allowed in object destructuring`, prop.value.loc);
92
320
  }
93
321
  });
94
322
  }
323
+ function objectAssignmentPatternSteps(pat, initializerExpression, keyExpression) {
324
+ if (pat.left.type !== AST_NODE_TYPES.Identifier) {
325
+ throw new WorkflowSyntaxError('Default value can be used only with an identifier', pat.left.loc);
326
+ }
327
+ // Using Switch step instead of default() because pat.right must be evaluated only
328
+ // in the default value branch (in case it has side effects)
329
+ const name = new VariableReferenceExpression(pat.left.name);
330
+ return [
331
+ new SwitchStepAST([
332
+ {
333
+ condition: new BinaryExpression(new PrimitiveExpression(pat.left.name), 'in', initializerExpression),
334
+ steps: [new AssignStepAST([{ name, value: keyExpression }])],
335
+ },
336
+ {
337
+ condition: trueEx,
338
+ steps: [
339
+ new AssignStepAST([{ name, value: convertExpression(pat.right) }]),
340
+ ],
341
+ },
342
+ ]),
343
+ ];
344
+ }
345
+ function objectDestructuringRestSteps(properties, rest, initializerExpression) {
346
+ throwIfInvalidRestElement(properties);
347
+ if (rest.argument.type !== AST_NODE_TYPES.Identifier) {
348
+ throw new WorkflowSyntaxError('Identifier expected', rest.argument.loc);
349
+ }
350
+ const nonRestProperties = properties.filter((x) => x.type !== AST_NODE_TYPES.RestElement);
351
+ const nonRestKeys = nonRestProperties
352
+ .map((p) => p.key)
353
+ .map((p) => {
354
+ if (p.type !== AST_NODE_TYPES.Identifier) {
355
+ throw new WorkflowSyntaxError('Identifier expected', p.loc);
356
+ }
357
+ return p;
358
+ })
359
+ .map((p) => p.name);
360
+ const name = new VariableReferenceExpression(rest.argument.name);
361
+ const value = nonRestKeys.reduce((acc, propertyName) =>
362
+ // map.delete returns a copy of the object and removes the specified property
363
+ new FunctionInvocationExpression('map.delete', [
364
+ acc,
365
+ new PrimitiveExpression(propertyName),
366
+ ]), initializerExpression);
367
+ return [new AssignStepAST([{ name, value }])];
368
+ }
95
369
  function assignmentExpressionToSteps(node, ctx) {
96
370
  let compoundOperator = undefined;
97
371
  switch (node.operator) {
@@ -122,44 +396,138 @@ function assignmentExpressionToSteps(node, ctx) {
122
396
  default:
123
397
  throw new WorkflowSyntaxError(`Operator ${node.operator} is not supported in assignment expressions`, node.loc);
124
398
  }
125
- const targetExpression = convertExpression(node.left);
126
- if (!isFullyQualifiedName(targetExpression)) {
127
- throw new WorkflowSyntaxError('The left-hand side of an assignment must be an identifier or a property access', node.loc);
399
+ if (compoundOperator === undefined) {
400
+ return assignmentSteps(node.left, node.right, ctx);
401
+ }
402
+ else {
403
+ return compoundAssignmentSteps(node.left, node.right, compoundOperator, ctx);
128
404
  }
405
+ }
406
+ function assignmentSteps(left, right, ctx) {
129
407
  let valueExpression;
130
408
  const steps = [];
131
- const targetName = targetExpression.toString();
132
- if (node.right.type === AST_NODE_TYPES.CallExpression &&
133
- node.right.callee.type === AST_NODE_TYPES.Identifier &&
134
- isMagicFunction(node.right.callee.name)) {
135
- const calleeName = node.right.callee.name;
136
- if (isMagicFunctionStatmentOnly(calleeName)) {
137
- throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, node.right.callee.loc);
138
- }
139
- const needsTempVariable = compoundOperator === undefined ||
140
- node.left.type !== AST_NODE_TYPES.Identifier;
141
- const resultVariable = needsTempVariable ? '__temp' : targetName;
142
- steps.push(...callExpressionToStep(node.right, resultVariable, ctx));
143
- if (!needsTempVariable) {
144
- return steps;
145
- }
146
- valueExpression = new VariableReferenceExpression('__temp');
409
+ if (left.type === AST_NODE_TYPES.ArrayPattern) {
410
+ return convertArrayDestructuring(left, right, ctx);
411
+ }
412
+ else if (left.type === AST_NODE_TYPES.ObjectPattern) {
413
+ return convertObjectDestructuring(left, right, ctx);
414
+ }
415
+ if (right.type === AST_NODE_TYPES.CallExpression &&
416
+ right.callee.type === AST_NODE_TYPES.Identifier &&
417
+ isIntrinsic(right.callee.name)) {
418
+ const tr = convertAssignmentExpressionIntrinsicRHS(right, ctx);
419
+ steps.push(...tr.steps);
420
+ valueExpression = tr.tempVariable;
147
421
  }
148
422
  else {
149
- valueExpression = convertExpression(node.right);
423
+ valueExpression = convertExpression(right);
424
+ }
425
+ const targetExpression = convertVariableNameExpression(left);
426
+ steps.push(new AssignStepAST([{ name: targetExpression, value: valueExpression }]));
427
+ return steps;
428
+ }
429
+ function compoundAssignmentSteps(left, right, operator, ctx) {
430
+ let valueExpression;
431
+ const { expression: targetExpression, steps } = convertCompoundAssignmentLeftHandSide(left, ctx);
432
+ if (right.type === AST_NODE_TYPES.CallExpression &&
433
+ right.callee.type === AST_NODE_TYPES.Identifier &&
434
+ isIntrinsic(right.callee.name)) {
435
+ const tr = convertAssignmentExpressionIntrinsicRHS(right, ctx);
436
+ steps.push(...tr.steps);
437
+ valueExpression = tr.tempVariable;
150
438
  }
151
- if (compoundOperator) {
152
- valueExpression = new BinaryExpression(new VariableReferenceExpression(targetName), compoundOperator, valueExpression);
439
+ else {
440
+ valueExpression = convertExpression(right);
153
441
  }
154
- steps.push(new AssignStepAST([[targetName, valueExpression]]));
442
+ valueExpression = new BinaryExpression(targetExpression, operator, valueExpression);
443
+ steps.push(new AssignStepAST([{ name: targetExpression, value: valueExpression }]));
155
444
  return steps;
156
445
  }
446
+ function convertCompoundAssignmentLeftHandSide(left, ctx) {
447
+ if (left.type === AST_NODE_TYPES.ArrayPattern ||
448
+ left.type === AST_NODE_TYPES.ObjectPattern) {
449
+ throw new WorkflowSyntaxError(`Invalid left-hand side in assignment`, left.loc);
450
+ }
451
+ const leftEx = convertVariableNameExpression(left);
452
+ if (leftEx.expressionType === 'member') {
453
+ const { transformed, assignments } = extractSideEffectsFromMemberExpression(leftEx, tempName(ctx), 0);
454
+ const steps = [new AssignStepAST(assignments)];
455
+ return { expression: transformed, steps };
456
+ }
457
+ else {
458
+ return {
459
+ expression: leftEx,
460
+ steps: [],
461
+ };
462
+ }
463
+ }
464
+ /**
465
+ * Extract side-effecting computed properties into temporary variable assignments.
466
+ *
467
+ * This is used on the left-hand side of a compound assignment expression, which
468
+ * should only be evaluted once.
469
+ */
470
+ function extractSideEffectsFromMemberExpression(ex, tempPrefix, tempIndex) {
471
+ if (ex.computed && !isPure(ex.property)) {
472
+ let transformedObject;
473
+ let objectAssignments;
474
+ if (ex.object.expressionType === 'member') {
475
+ const object2 = extractSideEffectsFromMemberExpression(ex.object, tempPrefix, tempIndex + 1);
476
+ transformedObject = object2.transformed;
477
+ objectAssignments = object2.assignments;
478
+ }
479
+ else {
480
+ transformedObject = ex.object;
481
+ objectAssignments = [];
482
+ }
483
+ const tmp = new VariableReferenceExpression(`${tempPrefix}${tempIndex}`);
484
+ const transformed = new MemberExpression(transformedObject, tmp, true);
485
+ const assignments = objectAssignments;
486
+ assignments.push({
487
+ name: tmp,
488
+ value: ex.property,
489
+ });
490
+ return { transformed, assignments };
491
+ }
492
+ else if (ex.object.expressionType === 'member') {
493
+ const { transformed: object2, assignments: assignments } = extractSideEffectsFromMemberExpression(ex.object, tempPrefix, tempIndex);
494
+ const transformed = new MemberExpression(object2, ex.property, ex.computed);
495
+ return {
496
+ transformed,
497
+ assignments,
498
+ };
499
+ }
500
+ else {
501
+ return {
502
+ transformed: ex,
503
+ assignments: [],
504
+ };
505
+ }
506
+ }
507
+ /**
508
+ * Special case for handling call_step() RHS in assignment expressions.
509
+ *
510
+ * This can be removed once the generic convertExpression() is able to handle call_step.
511
+ */
512
+ function convertAssignmentExpressionIntrinsicRHS(callEx, ctx) {
513
+ if (callEx.callee.type !== AST_NODE_TYPES.Identifier) {
514
+ throw new InternalTranspilingError('The callee should be an identifier');
515
+ }
516
+ const calleeName = callEx.callee.name;
517
+ if (isIntrinsicStatement(calleeName)) {
518
+ throw new WorkflowSyntaxError(`"${calleeName}" can't be called as part of an expression`, callEx.callee.loc);
519
+ }
520
+ const resultVariable = tempName(ctx);
521
+ const steps = callExpressionToStep(callEx, resultVariable, ctx);
522
+ const tempVariable = new VariableReferenceExpression(resultVariable);
523
+ return { steps, tempVariable };
524
+ }
157
525
  function callExpressionToStep(node, resultVariable, ctx) {
158
526
  const calleeExpression = convertExpression(node.callee);
159
527
  if (isFullyQualifiedName(calleeExpression)) {
160
528
  const calleeName = calleeExpression.toString();
161
529
  if (calleeName === 'parallel') {
162
- // A custom implementation for "parallel"
530
+ // A handle the "parallel" intrinsic
163
531
  return [callExpressionToParallelStep(node, ctx)];
164
532
  }
165
533
  else if (calleeName === 'retry_policy') {
@@ -176,8 +544,9 @@ function callExpressionToStep(node, resultVariable, ctx) {
176
544
  ];
177
545
  }
178
546
  else {
547
+ const resultVariable2 = resultVariable ?? tempName(ctx);
179
548
  return [
180
- callExpressionAssignStep(calleeName, node.arguments, resultVariable),
549
+ callExpressionAssignStep(calleeName, node.arguments, resultVariable2),
181
550
  ];
182
551
  }
183
552
  }
@@ -188,10 +557,10 @@ function callExpressionToStep(node, resultVariable, ctx) {
188
557
  function callExpressionAssignStep(functionName, argumentsNode, resultVariable) {
189
558
  const argumentExpressions = throwIfSpread(argumentsNode).map(convertExpression);
190
559
  return new AssignStepAST([
191
- [
192
- resultVariable ?? '__temp',
193
- new FunctionInvocationExpression(functionName, argumentExpressions),
194
- ],
560
+ {
561
+ name: new VariableReferenceExpression(resultVariable),
562
+ value: new FunctionInvocationExpression(functionName, argumentExpressions),
563
+ },
195
564
  ]);
196
565
  }
197
566
  function createCallStep(node, argumentsNode, resultVariable) {
@@ -245,14 +614,19 @@ function callExpressionToParallelStep(node, ctx) {
245
614
  node.callee.name !== 'parallel') {
246
615
  throw new InternalTranspilingError(`The parameter must be a call to "parallel"`);
247
616
  }
617
+ const ctx2 = Object.assign({}, ctx, {
618
+ parallelNestingLevel: ctx.parallelNestingLevel
619
+ ? ctx.parallelNestingLevel + 1
620
+ : 1,
621
+ });
248
622
  let steps = {};
249
623
  if (node.arguments.length > 0) {
250
624
  switch (node.arguments[0].type) {
251
625
  case AST_NODE_TYPES.ArrayExpression:
252
- steps = parseParallelBranches(node.arguments[0]);
626
+ steps = parseParallelBranches(node.arguments[0], ctx2);
253
627
  break;
254
628
  case AST_NODE_TYPES.ArrowFunctionExpression:
255
- steps = parseParallelIteration(node.arguments[0], ctx);
629
+ steps = parseParallelIteration(node.arguments[0], ctx2);
256
630
  break;
257
631
  default:
258
632
  throw new WorkflowSyntaxError('The first parameter must be an array of functions or an arrow function', node.arguments[0].loc);
@@ -269,25 +643,21 @@ function callExpressionToParallelStep(node, ctx) {
269
643
  }
270
644
  return new ParallelStepAST(steps, shared, concurrencyLimit, exceptionPolicy);
271
645
  }
272
- function parseParallelBranches(node) {
273
- const stepsArray = node.elements.map((arg, idx) => {
274
- const branchName = `branch${idx + 1}`;
275
- if (arg === null) {
276
- throw new WorkflowSyntaxError('Argument should be a function call of type () => void', node.loc);
277
- }
278
- switch (arg.type) {
646
+ function parseParallelBranches(node, ctx) {
647
+ const branches = node.elements.map((arg) => {
648
+ switch (arg?.type) {
279
649
  case AST_NODE_TYPES.Identifier:
280
- return [branchName, new StepsStepAST([new CallStepAST(arg.name)])];
650
+ return new StepsStepAST([new CallStepAST(arg.name)]);
281
651
  case AST_NODE_TYPES.ArrowFunctionExpression:
282
652
  if (arg.body.type !== AST_NODE_TYPES.BlockStatement) {
283
653
  throw new WorkflowSyntaxError('The body must be a block statement', arg.body.loc);
284
654
  }
285
- return [branchName, new StepsStepAST(parseStatement(arg.body, {}))];
655
+ return new StepsStepAST(parseStatement(arg.body, ctx));
286
656
  default:
287
- throw new WorkflowSyntaxError('Argument should be a function call of type () => void', arg.loc);
657
+ throw new WorkflowSyntaxError('Argument should be a function of type () => void', arg ? arg.loc : node.loc);
288
658
  }
289
659
  });
290
- return Object.fromEntries(stepsArray);
660
+ return Object.fromEntries(branches.map((step, i) => [`branch${i + 1}`, step]));
291
661
  }
292
662
  function parseParallelIteration(node, ctx) {
293
663
  if (node.body.type !== AST_NODE_TYPES.BlockStatement ||
@@ -330,8 +700,13 @@ function parseParallelOptions(node) {
330
700
  exceptionPolicy,
331
701
  };
332
702
  }
333
- function generalExpressionToAssignStep(node) {
334
- return new AssignStepAST([['__temp', convertExpression(node)]]);
703
+ function generalExpressionToAssignStep(node, ctx) {
704
+ return new AssignStepAST([
705
+ {
706
+ name: new VariableReferenceExpression(tempName(ctx)),
707
+ value: convertExpression(node),
708
+ },
709
+ ]);
335
710
  }
336
711
  function returnStatementToReturnStep(node, ctx) {
337
712
  const value = node.argument ? convertExpression(node.argument) : undefined;
@@ -361,7 +736,7 @@ function flattenIfBranches(ifStatement, ctx) {
361
736
  }
362
737
  else {
363
738
  branches.push({
364
- condition: new PrimitiveExpression(true),
739
+ condition: trueEx,
365
740
  steps: parseStatement(ifStatement.alternate, ctx),
366
741
  });
367
742
  }
@@ -381,10 +756,10 @@ function switchStatementToSteps(node, ctx) {
381
756
  condition = new BinaryExpression(discriminant, '==', test);
382
757
  }
383
758
  else {
384
- condition = new PrimitiveExpression(true);
759
+ condition = trueEx;
385
760
  }
386
761
  const jumpTarget = new JumpTargetAST();
387
- const body = transformAST(caseNode.consequent.flatMap((x) => parseStatement(x, switchCtx)));
762
+ const body = caseNode.consequent.flatMap((x) => parseStatement(x, switchCtx));
388
763
  steps.push(jumpTarget);
389
764
  steps.push(...body);
390
765
  branches.push({
@@ -416,12 +791,12 @@ function forOfStatementToForStep(node, ctx) {
416
791
  throw new WorkflowSyntaxError('Initial value not allowed', declaration.init.loc);
417
792
  }
418
793
  if (declaration.id.type !== AST_NODE_TYPES.Identifier) {
419
- throw new WorkflowSyntaxError(`Expected identifier, got ${declaration.id.type}`, declaration.id.loc);
794
+ throw new WorkflowSyntaxError(`Identifier expected, got ${declaration.id.type}`, declaration.id.loc);
420
795
  }
421
796
  loopVariableName = declaration.id.name;
422
797
  }
423
798
  else {
424
- throw new InternalTranspilingError(`Expected Identifier or VariableDeclaration, got ${node.left.type}`);
799
+ throw new InternalTranspilingError(`Identifier or VariableDeclaration expected, got ${node.left.type}`);
425
800
  }
426
801
  const listExpression = convertExpression(node.right);
427
802
  if (isLiteral(listExpression) &&
@@ -443,7 +818,7 @@ function whileStatementSteps(node, ctx) {
443
818
  breakTarget: endOfLoop.label,
444
819
  });
445
820
  const postSteps = [new NextStepAST(startOfLoop.label)];
446
- const steps = parseStatement(node.body, ctx2, postSteps);
821
+ const steps = parseStatement(node.body, ctx2).concat(postSteps);
447
822
  return [
448
823
  startOfLoop,
449
824
  new SwitchStepAST([
@@ -583,8 +958,14 @@ function extractErrorVariableName(param) {
583
958
  */
584
959
  function finalizerInitializer(conditionVariable, valueVariable) {
585
960
  return new AssignStepAST([
586
- [conditionVariable, new PrimitiveExpression(null)],
587
- [valueVariable, new PrimitiveExpression(null)],
961
+ {
962
+ name: new VariableReferenceExpression(conditionVariable),
963
+ value: nullEx,
964
+ },
965
+ {
966
+ name: new VariableReferenceExpression(valueVariable),
967
+ value: nullEx,
968
+ },
588
969
  ]);
589
970
  }
590
971
  /**
@@ -600,27 +981,32 @@ function finalizerInitializer(conditionVariable, valueVariable) {
600
981
  * }
601
982
  */
602
983
  function finalizerFooter(conditionVariable, valueVariable) {
984
+ const variable = new VariableReferenceExpression(conditionVariable);
985
+ const val = new VariableReferenceExpression(valueVariable);
986
+ const returnString = new PrimitiveExpression('return');
987
+ const raiseString = new PrimitiveExpression('raise');
603
988
  return new SwitchStepAST([
604
989
  {
605
- condition: new BinaryExpression(new VariableReferenceExpression(conditionVariable), '==', new PrimitiveExpression('return')),
606
- steps: [
607
- new ReturnStepAST(new VariableReferenceExpression(valueVariable)),
608
- ],
990
+ condition: new BinaryExpression(variable, '==', returnString),
991
+ steps: [new ReturnStepAST(val)],
609
992
  },
610
993
  {
611
- condition: new BinaryExpression(new VariableReferenceExpression(conditionVariable), '==', new PrimitiveExpression('raise')),
612
- steps: [new RaiseStepAST(new VariableReferenceExpression(valueVariable))],
994
+ condition: new BinaryExpression(variable, '==', raiseString),
995
+ steps: [new RaiseStepAST(val)],
613
996
  },
614
997
  ]);
615
998
  }
616
999
  function finalizerDelayedException(exceptionVariableName, conditionVariableName, valueVariableName) {
617
1000
  return [
618
1001
  new AssignStepAST([
619
- [conditionVariableName, new PrimitiveExpression('raise')],
620
- [
621
- valueVariableName,
622
- new VariableReferenceExpression(exceptionVariableName),
623
- ],
1002
+ {
1003
+ name: new VariableReferenceExpression(conditionVariableName),
1004
+ value: new PrimitiveExpression('raise'),
1005
+ },
1006
+ {
1007
+ name: new VariableReferenceExpression(valueVariableName),
1008
+ value: new VariableReferenceExpression(exceptionVariableName),
1009
+ },
624
1010
  ]),
625
1011
  ];
626
1012
  }
@@ -630,8 +1016,14 @@ function delayedReturnAndJumpToFinalizer(value, ctx) {
630
1016
  : undefined;
631
1017
  const [conditionVariable, valueVariable] = finalizerVariables(ctx);
632
1018
  return new AssignStepAST([
633
- [conditionVariable, new PrimitiveExpression('return')],
634
- [valueVariable, value ?? new PrimitiveExpression(null)],
1019
+ {
1020
+ name: new VariableReferenceExpression(conditionVariable),
1021
+ value: new PrimitiveExpression('return'),
1022
+ },
1023
+ {
1024
+ name: new VariableReferenceExpression(valueVariable),
1025
+ value: value ?? nullEx,
1026
+ },
635
1027
  ], finalizerTarget);
636
1028
  }
637
1029
  function labeledStep(node, ctx) {
@@ -664,43 +1056,45 @@ function parseRetryPolicy(node) {
664
1056
  }
665
1057
  }
666
1058
  function retryPolicyFromParams(paramsObject, argsLoc) {
667
- const params = mapRecordValues(paramsObject, asExpression);
668
- if ('backoff' in params) {
669
- let predicate = '';
670
- const predicateEx = params.predicate;
671
- if (predicateEx === undefined) {
672
- predicate = undefined;
673
- }
674
- else if (isFullyQualifiedName(predicateEx)) {
675
- predicate = predicateEx.toString();
676
- }
677
- else {
678
- throw new WorkflowSyntaxError('"predicate" must be a function name', argsLoc);
679
- }
680
- const backoffEx = params.backoff;
681
- if (backoffEx.expressionType === 'primitive' && isRecord(backoffEx.value)) {
682
- const backoffLit = backoffEx.value;
683
- return {
684
- predicate,
685
- maxRetries: params.max_retries,
686
- backoff: {
687
- initialDelay: backoffLit.initial_delay
688
- ? asExpression(backoffLit.initial_delay)
689
- : undefined,
690
- maxDelay: backoffLit.max_delay
691
- ? asExpression(backoffLit.max_delay)
692
- : undefined,
693
- multiplier: backoffLit.multiplier
694
- ? asExpression(backoffLit.multiplier)
695
- : undefined,
696
- },
697
- };
698
- }
699
- else {
700
- throw new WorkflowSyntaxError('Expected an object literal', argsLoc);
701
- }
1059
+ const params = R.map(asExpression, paramsObject);
1060
+ if (!('backoff' in params)) {
1061
+ throw new WorkflowSyntaxError('Required parameter "backoff" missing', argsLoc);
1062
+ }
1063
+ else if (params.backoff.expressionType !== 'primitive' ||
1064
+ !isRecord(params.backoff.value)) {
1065
+ throw new WorkflowSyntaxError('Expected "backoff" to be an object literal', argsLoc);
1066
+ }
1067
+ const backoff = params.backoff.value;
1068
+ return {
1069
+ predicate: predicateFromRetryParams(params, argsLoc),
1070
+ maxRetries: params.max_retries,
1071
+ backoff: {
1072
+ initialDelay: safeAsExpression(backoff.initial_delay),
1073
+ maxDelay: safeAsExpression(backoff.max_delay),
1074
+ multiplier: safeAsExpression(backoff.multiplier),
1075
+ },
1076
+ };
1077
+ }
1078
+ function predicateFromRetryParams(params, argsLoc) {
1079
+ if (!('predicate' in params)) {
1080
+ return undefined;
1081
+ }
1082
+ else if (isFullyQualifiedName(params.predicate)) {
1083
+ return params.predicate.toString();
1084
+ }
1085
+ else {
1086
+ throw new WorkflowSyntaxError('"predicate" must be a function name', argsLoc);
1087
+ }
1088
+ }
1089
+ function tempName(ctx) {
1090
+ if (ctx.parallelNestingLevel !== undefined) {
1091
+ // Temporary variable inside a parallel step can not be the same as temporary
1092
+ // variables on the outside. Sharing the variable name would cause deployment
1093
+ // error, if the variable is not marked as shared by including it in the
1094
+ // "shared" array.
1095
+ return `__temp_parallel${ctx.parallelNestingLevel ?? 0}`;
702
1096
  }
703
1097
  else {
704
- throw new WorkflowSyntaxError('Some required retry policy parameters are missing', argsLoc);
1098
+ return '__temp';
705
1099
  }
706
1100
  }