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.
Files changed (53) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +36 -9
  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 -521
  12. package/dist/ast/workflows.d.ts +14 -12
  13. package/dist/ast/workflows.d.ts.map +1 -1
  14. package/dist/ast/workflows.js +20 -35
  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 +19 -1
  20. package/dist/transpiler/index.d.ts.map +1 -1
  21. package/dist/transpiler/index.js +158 -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 +41 -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 +53 -17
  38. package/package.json +15 -11
  39. package/types/workflowslib.d.ts +58 -74
  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/ast/validation.d.ts +0 -20
  44. package/dist/ast/validation.d.ts.map +0 -1
  45. package/dist/ast/validation.js +0 -214
  46. package/dist/transpiler/expressions.d.ts +0 -11
  47. package/dist/transpiler/expressions.d.ts.map +0 -1
  48. package/dist/transpiler/statements.d.ts +0 -10
  49. package/dist/transpiler/statements.d.ts.map +0 -1
  50. package/dist/transpiler/statements.js +0 -1100
  51. package/dist/utils.d.ts +0 -9
  52. package/dist/utils.d.ts.map +0 -1
  53. package/dist/utils.js +0 -13
@@ -1,342 +1,331 @@
1
1
  import * as R from 'ramda';
2
- import { AssignStepAST, CallStepAST, ForStepAST, RaiseStepAST, ReturnStepAST, SwitchStepAST, } from '../ast/steps.js';
3
2
  import { InternalTranspilingError } from '../errors.js';
4
- import { isRecord } from '../utils.js';
5
- import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, UnaryExpression, VariableReferenceExpression, asExpression, isExpression, isLiteral, } from '../ast/expressions.js';
3
+ import { binaryEx, functionInvocationEx, listEx, mapEx, memberEx, stringEx, unaryEx, variableReferenceEx, } from '../ast/expressions.js';
4
+ import { applyNested, AssignStatement, FunctionInvocationStatement, IfStatement, LabelledStatement, } from '../ast/statements.js';
6
5
  import { blockingFunctions } from './generated/functionMetadata.js';
7
6
  /**
8
7
  * Performs various transformations on the AST.
9
8
  */
10
- const transformPipe = R.pipe(mapLiteralsAsAssignSteps, mergeAssignSteps, flattenPlainNextConditions, intrinsicFunctionImplementation, blockingCallsAsCallSteps);
11
- export function transformAST(steps) {
12
- return transformPipe(steps.map((x) => x.applyNestedSteps(transformAST)));
9
+ export function transformAST(statements) {
10
+ const tempGen = createTempVariableGenerator();
11
+ const transform = R.pipe(R.chain(mapLiteralsAsAssigns(tempGen)), mergeAssigns, R.chain(intrinsicFunctionImplementation), R.chain(blockingCallsAsFunctionCalls(tempGen)));
12
+ return transform(statements.map((s) => applyNested(transformAST, s)));
13
13
  }
14
14
  /**
15
- * Merge consecutive assign steps into one assign step
15
+ * Merge consecutive assign statements into one assign statement
16
16
  *
17
- * An assign step can contain up to 50 assignments.
17
+ * An assign is allowed to contain up to 50 assignments.
18
18
  */
19
- function mergeAssignSteps(steps) {
20
- return steps.reduce((acc, current) => {
19
+ function mergeAssigns(statements) {
20
+ return statements.reduce((acc, current) => {
21
21
  const prev = acc.length > 0 ? acc[acc.length - 1] : null;
22
- if (current.tag === 'assign' &&
23
- prev?.tag === 'assign' &&
24
- prev.assignments.length < 50 &&
25
- !prev.next) {
26
- const merged = new AssignStepAST(prev.assignments.concat(current.assignments), current.next, prev.label ?? current.label);
27
- acc.pop();
28
- acc.push(merged);
22
+ let prevAssignments = undefined;
23
+ let label = undefined;
24
+ if (prev?.tag === 'assign') {
25
+ prevAssignments = prev.assignments;
29
26
  }
30
- else {
31
- acc.push(current);
27
+ else if (prev?.tag === 'label' &&
28
+ prev.statements.every((x) => x.tag === 'assign')) {
29
+ prevAssignments = prev.statements.flatMap((x) => x.assignments);
30
+ label = prev.label;
32
31
  }
33
- return acc;
34
- }, []);
35
- }
36
- /**
37
- * Merge a next step to the previous step.
38
- *
39
- * For example, transforms this:
40
- *
41
- * switch:
42
- * - condition: ${x > 0}
43
- * steps:
44
- * - next1:
45
- * steps:
46
- * next: target1
47
- *
48
- * into this:
49
- *
50
- * switch:
51
- * - condition: ${x > 0}
52
- * next: target1
53
- */
54
- export function flattenPlainNextConditions(steps) {
55
- return steps.reduce((acc, step) => {
56
- if (acc.length > 0 && step.tag === 'next') {
57
- // Merge a "next" to the previous "assign" step
58
- const prev = acc[acc.length - 1];
59
- if (prev.tag === 'assign' && !prev.next) {
60
- acc.pop();
61
- acc.push(prev.withNext(step.target));
62
- }
63
- else {
64
- acc.push(step);
65
- }
66
- }
67
- else if (step.tag === 'switch') {
68
- // If the condition steps consists of a single "next", merge it with the condition
69
- acc.push(flattenNextToCondition(step));
32
+ let currentAssignments = undefined;
33
+ if (current.tag === 'assign') {
34
+ currentAssignments = current.assignments;
70
35
  }
71
- else {
72
- acc.push(step);
73
- }
74
- return acc;
75
- }, []);
76
- }
77
- function flattenNextToCondition(step) {
78
- const transformedBranches = step.branches.map((cond) => {
79
- if (!cond.next && cond.steps.length === 1 && cond.steps[0].tag === 'next') {
80
- const nextStep = cond.steps[0];
81
- return {
82
- condition: cond.condition,
83
- steps: [],
84
- next: nextStep.target,
85
- };
86
- }
87
- else {
88
- return cond;
89
- }
90
- });
91
- return new SwitchStepAST(transformedBranches);
92
- }
93
- /**
94
- * Search for blocking calls in expressions and replace them with call step + variable.
95
- *
96
- * The Workflows runtime requires that blocking calls must be executed by call steps.
97
- *
98
- * For example, transforms this:
99
- *
100
- * - return1:
101
- * return: ${http.get("https://example.com")}
102
- *
103
- * into this:
104
- *
105
- * - call_http_get_1:
106
- * call: http.get
107
- * args:
108
- * url: https://example.com
109
- * result: __temp0
110
- * - return1:
111
- * return: ${__temp0}
112
- */
113
- function blockingCallsAsCallSteps(steps) {
114
- const transform = transformStepExpressions(replaceBlockingCalls);
115
- return R.chain(transform, steps);
116
- }
117
- function createTempVariableGenerator() {
118
- let i = 0;
119
- return () => `__temp${i++}`;
120
- }
121
- function replaceBlockingCalls(expression) {
122
- function replaceBlockingFunctionInvocations(ex) {
123
- if (ex.expressionType !== 'functionInvocation') {
124
- return ex;
36
+ else if (current.tag === 'label' &&
37
+ current.statements.every((x) => x.tag === 'assign')) {
38
+ currentAssignments = current.statements.flatMap((x) => x.assignments);
39
+ label ??= current.label;
125
40
  }
126
- const blockingCallArgumentNames = blockingFunctions.get(ex.functionName);
127
- if (blockingCallArgumentNames) {
128
- if (ex.arguments.length > blockingCallArgumentNames.length) {
129
- throw new InternalTranspilingError('FunctionInvocationTerm has more arguments than metadata allows!');
41
+ if (prevAssignments && currentAssignments) {
42
+ let merged = new AssignStatement(prevAssignments.concat(currentAssignments));
43
+ if (label) {
44
+ merged = new LabelledStatement(label, [merged]);
130
45
  }
131
- const nameAndValue = R.zip(blockingCallArgumentNames, ex.arguments);
132
- const callArgs = R.fromPairs(nameAndValue);
133
- const tempCallResultVariable = generateName();
134
- callSteps.push(new CallStepAST(ex.functionName, callArgs, tempCallResultVariable));
135
- // replace function invocation with a reference to the temporary variable
136
- return new VariableReferenceExpression(tempCallResultVariable);
46
+ acc.pop();
47
+ acc.push(merged);
137
48
  }
138
49
  else {
139
- return ex;
50
+ acc.push(current);
140
51
  }
141
- }
142
- const generateName = createTempVariableGenerator();
143
- const callSteps = [];
144
- return [
145
- callSteps,
146
- transformExpression(replaceBlockingFunctionInvocations, expression),
147
- ];
52
+ return acc;
53
+ }, []);
148
54
  }
149
55
  /**
150
- * Transform expressions in a step by applying transform.
56
+ * Transform expressions in a statement by applying transform.
151
57
  *
152
- * Returns an array of steps. The array includes the transformed step and possibly
153
- * additional steps constructed during the transformation. For example, a
154
- * transformation might extract blocking call expressions into call steps.
58
+ * Returns an array of statements. The array includes the transformed statement
59
+ * and possibly additional statements constructed during the transformation.
60
+ * For example, a transformation might extract blocking call expressions into
61
+ * function call.
155
62
  */
156
- const transformStepExpressions = R.curry(function (transform, step) {
157
- switch (step.tag) {
63
+ const expandExpressionToStatements = R.curry(function (transform, statement) {
64
+ switch (statement.tag) {
158
65
  case 'assign':
159
- return transformExpressionsAssign(transform, step);
160
- case 'call':
161
- return transformExpressionsCall(transform, step);
66
+ return expandAssign(transform, statement);
67
+ case 'function-invocation':
68
+ return expandFunctionInvocation(transform, statement);
162
69
  case 'for':
163
- return transformExpressionsFor(transform, step);
70
+ return expandFor(transform, statement);
71
+ case 'for-range':
72
+ return expandForRange(transform, statement);
73
+ case 'if':
74
+ return expandIf(transform, statement);
164
75
  case 'raise':
165
- return transformExpressionsRaise(transform, step);
76
+ return expandRaise(transform, statement);
166
77
  case 'return':
167
- return transformExpressionsReturn(transform, step);
78
+ return expanrdReturn(transform, statement);
168
79
  case 'switch':
169
- return transformExpressionsSwitch(transform, step);
170
- case 'forrange':
171
- case 'next':
80
+ return expandSwitch(transform, statement);
81
+ case 'while':
82
+ case 'do-while':
83
+ return expandWhile(transform, statement);
84
+ case 'break':
85
+ case 'continue':
86
+ case 'label':
172
87
  case 'parallel':
173
- case 'steps':
88
+ case 'parallel-for':
174
89
  case 'try':
175
- case 'jumptarget':
176
- return [step];
90
+ return [statement];
177
91
  }
178
92
  });
179
- function transformExpressionsAssign(transform, step) {
180
- if (step.assignments) {
181
- const newSteps = [];
182
- const newAssignments = step.assignments.map(({ name, value }) => {
183
- const [steps2, transformedKey] = transform(name);
184
- const [steps3, transformedValue] = transform(value);
185
- newSteps.push(...steps2);
186
- newSteps.push(...steps3);
187
- if (transformedKey.expressionType !== 'variableReference' &&
188
- transformedKey.expressionType !== 'member') {
189
- throw new InternalTranspilingError('Unexpected key type when transforming assign step');
190
- }
191
- return { name: transformedKey, value: transformedValue };
192
- });
193
- newSteps.push(new AssignStepAST(newAssignments, step.next, step.label));
194
- return newSteps;
195
- }
196
- else {
197
- return [step];
198
- }
93
+ function expandAssign(transform, statement) {
94
+ const newStatements = [];
95
+ const newAssignments = statement.assignments.map(({ name, value }) => {
96
+ const [st2, transformedKey] = transform(name);
97
+ const [st3, transformedValue] = transform(value);
98
+ newStatements.push(...st2);
99
+ newStatements.push(...st3);
100
+ if (transformedKey.tag !== 'variableReference' &&
101
+ transformedKey.tag !== 'member') {
102
+ throw new InternalTranspilingError('Unexpected key type when transforming an assignment');
103
+ }
104
+ return { name: transformedKey, value: transformedValue };
105
+ });
106
+ newStatements.push({ ...statement, assignments: newAssignments });
107
+ return newStatements;
199
108
  }
200
- function transformExpressionsCall(transform, step) {
201
- if (step.args) {
202
- const newSteps = [];
109
+ function expandFunctionInvocation(transform, statement) {
110
+ if (statement.args) {
111
+ const newStatements = [];
203
112
  const newArgs = R.map((ex) => {
204
- const [steps2, ex2] = transform(ex);
205
- newSteps.push(...steps2);
113
+ const [st2, ex2] = transform(ex);
114
+ newStatements.push(...st2);
206
115
  return ex2;
207
- }, step.args);
208
- newSteps.push(new CallStepAST(step.call, newArgs, step.result, step.label));
209
- return newSteps;
116
+ }, statement.args);
117
+ newStatements.push({ ...statement, args: newArgs });
118
+ return newStatements;
210
119
  }
211
120
  else {
212
- return [step];
121
+ return [statement];
213
122
  }
214
123
  }
215
- function transformExpressionsFor(transform, step) {
216
- if (step.listExpression) {
217
- const [newSteps, newListExpression] = transform(step.listExpression);
218
- newSteps.push(new ForStepAST(step.steps, step.loopVariableName, newListExpression, step.indexVariableName, step.label));
219
- return newSteps;
124
+ function expandFor(transform, statement) {
125
+ const [res, newListExpression] = transform(statement.listExpression);
126
+ res.push({ ...statement, listExpression: newListExpression });
127
+ return res;
128
+ }
129
+ function expandForRange(transform, statement) {
130
+ const res = [];
131
+ let newRangeStart;
132
+ let newRangeEnd;
133
+ if (typeof statement.rangeStart === 'number') {
134
+ newRangeStart = statement.rangeStart;
220
135
  }
221
136
  else {
222
- return [step];
137
+ const [st1, ex1] = transform(statement.rangeStart);
138
+ res.push(...st1);
139
+ newRangeStart = ex1;
223
140
  }
224
- }
225
- function transformExpressionsRaise(transform, step) {
226
- if (step.value) {
227
- const [newSteps, newEx] = transform(step.value);
228
- newSteps.push(new RaiseStepAST(newEx, step.label));
229
- return newSteps;
141
+ if (typeof statement.rangeEnd === 'number') {
142
+ newRangeEnd = statement.rangeEnd;
230
143
  }
231
144
  else {
232
- return [step];
145
+ const [st2, ex2] = transform(statement.rangeEnd);
146
+ res.push(...st2);
147
+ newRangeEnd = ex2;
233
148
  }
149
+ res.push({ ...statement, rangeStart: newRangeStart, rangeEnd: newRangeEnd });
150
+ return res;
151
+ }
152
+ function expandIf(transform, statement) {
153
+ const res = [];
154
+ const newBranches = statement.branches.map((branch) => {
155
+ const [st, ex] = transform(branch.condition);
156
+ res.push(...st);
157
+ return {
158
+ ...branch,
159
+ condition: ex,
160
+ };
161
+ });
162
+ res.push(new IfStatement(newBranches));
163
+ return res;
234
164
  }
235
- function transformExpressionsReturn(transform, step) {
236
- if (step.value) {
237
- const [newSteps, newEx] = transform(step.value);
238
- newSteps.push(new ReturnStepAST(newEx, step.label));
239
- return newSteps;
165
+ function expandRaise(transform, statement) {
166
+ const [res, newEx] = transform(statement.value);
167
+ res.push({ ...statement, value: newEx });
168
+ return res;
169
+ }
170
+ function expanrdReturn(transform, statement) {
171
+ if (statement.value) {
172
+ const [newStatements, newEx] = transform(statement.value);
173
+ newStatements.push({ ...statement, value: newEx });
174
+ return newStatements;
240
175
  }
241
176
  else {
242
- return [step];
177
+ return [statement];
243
178
  }
244
179
  }
245
- function transformExpressionsSwitch(transform, step) {
246
- const newSteps = [];
247
- const newBranches = step.branches.map((cond) => {
248
- const [steps2, ex2] = transform(cond.condition);
249
- newSteps.push(...steps2);
180
+ function expandSwitch(transform, statement) {
181
+ const res = [];
182
+ const newBranches = statement.branches.map((branch) => {
183
+ const [st2, ex2] = transform(branch.condition);
184
+ res.push(...st2);
250
185
  return {
251
186
  condition: ex2,
252
- steps: cond.steps,
253
- next: cond.next,
187
+ body: branch.body,
254
188
  };
255
189
  });
256
- newSteps.push(new SwitchStepAST(newBranches, step.label));
257
- return newSteps;
190
+ res.push({ ...statement, branches: newBranches });
191
+ return res;
192
+ }
193
+ function expandWhile(transform, statement) {
194
+ const [res, newCond] = transform(statement.condition);
195
+ res.push({ ...statement, condition: newCond });
196
+ return res;
197
+ }
198
+ function createTempVariableGenerator() {
199
+ let i = 0;
200
+ return () => `__temp${i++}`;
201
+ }
202
+ function replaceBlockingCalls(generateName, expression) {
203
+ function replaceBlockingFunctionInvocations(ex) {
204
+ if (ex.tag !== 'functionInvocation') {
205
+ return ex;
206
+ }
207
+ const blockingCallArgumentNames = blockingFunctions.get(ex.functionName);
208
+ if (blockingCallArgumentNames) {
209
+ const nameAndValue = R.zip(blockingCallArgumentNames, ex.arguments);
210
+ const callArgs = R.fromPairs(nameAndValue);
211
+ const tempCallResultVariable = generateName();
212
+ callStatements.push(new FunctionInvocationStatement(ex.functionName, callArgs, tempCallResultVariable));
213
+ // replace function invocation with a reference to the temporary variable
214
+ return variableReferenceEx(tempCallResultVariable);
215
+ }
216
+ else {
217
+ return ex;
218
+ }
219
+ }
220
+ const callStatements = [];
221
+ return [
222
+ callStatements,
223
+ transformExpression(replaceBlockingFunctionInvocations, expression),
224
+ ];
225
+ }
226
+ /**
227
+ * Search for blocking calls in expressions and replace them with a call + variable.
228
+ *
229
+ * The Workflows runtime requires that blocking calls must be executed by a call step.
230
+ *
231
+ * For example, transforms this:
232
+ *
233
+ * ```yaml
234
+ * - return1:
235
+ * return: ${http.get("https://example.com")}
236
+ * ```
237
+ *
238
+ * into this:
239
+ *
240
+ * ```yaml
241
+ * - call_http_get_1:
242
+ * call: http.get
243
+ * args:
244
+ * url: https://example.com
245
+ * result: __temp0
246
+ * - return1:
247
+ * return: ${__temp0}
248
+ * ```
249
+ */
250
+ function blockingCallsAsFunctionCalls(generateTempName) {
251
+ return (statement) => {
252
+ return expandExpressionToStatements((ex) => replaceBlockingCalls(generateTempName, ex), statement);
253
+ };
258
254
  }
259
255
  /**
260
256
  * Apply transform to expressions recursively.
261
- * Transform leaf expresionssins first.
257
+ * Transform leaf expressions first.
262
258
  */
263
259
  function transformExpression(transform, ex) {
264
- return transform(transformNestedExpressions(transform, ex));
265
- }
266
- function transformNestedExpressions(transform, ex) {
267
- const tr = (y) => transformExpression(transform, y);
268
- switch (ex.expressionType) {
269
- case 'primitive':
270
- if (isLiteral(ex)) {
271
- return ex;
272
- }
273
- else {
274
- const newPrimitive = transformPrimitive(transform, ex.value);
275
- return newPrimitive === ex.value
276
- ? ex
277
- : new PrimitiveExpression(newPrimitive);
278
- }
279
- case 'binary':
280
- return new BinaryExpression(tr(ex.left), ex.binaryOperator, tr(ex.right));
260
+ const nestedTr = (y) => transformExpression(transform, y);
261
+ switch (ex.tag) {
262
+ case 'string':
263
+ case 'number':
264
+ case 'boolean':
265
+ case 'null':
281
266
  case 'variableReference':
282
- return ex;
267
+ return transform(ex);
268
+ case 'list':
269
+ return transform(listEx(R.map(nestedTr, ex.value)));
270
+ case 'map':
271
+ return transform(mapEx(R.map(nestedTr, ex.value)));
272
+ case 'binary':
273
+ return transform(binaryEx(nestedTr(ex.left), ex.binaryOperator, nestedTr(ex.right)));
283
274
  case 'functionInvocation':
284
- return new FunctionInvocationExpression(ex.functionName, ex.arguments.map(tr));
275
+ return transform(functionInvocationEx(ex.functionName, ex.arguments.map(nestedTr)));
285
276
  case 'member':
286
- return new MemberExpression(tr(ex.object), tr(ex.property), ex.computed);
277
+ return transform(memberEx(nestedTr(ex.object), nestedTr(ex.property), ex.computed));
287
278
  case 'unary':
288
- return new UnaryExpression(ex.operator, tr(ex.value));
289
- }
290
- }
291
- function transformPrimitive(transform, val) {
292
- const tranformVal = R.ifElse(isExpression, (x) => transformExpression(transform, x), (x) => transformPrimitive(transform, x));
293
- if (Array.isArray(val)) {
294
- return R.map(tranformVal, val);
295
- }
296
- else if (isRecord(val)) {
297
- return R.map(tranformVal, val);
298
- }
299
- else {
300
- return val;
279
+ return transform(unaryEx(ex.operator, nestedTr(ex.value)));
301
280
  }
302
281
  }
303
282
  /**
304
- * Search for map literals in expressions and replace them with assign step + variable.
283
+ * Search for map literals in expressions and replace them with assign + variable.
305
284
  *
306
285
  * Workflows does not support a map literal in expressions.
307
286
  *
308
287
  * For example, transforms this:
309
288
  *
289
+ * ```yaml
310
290
  * - return1:
311
291
  * return: ${ {value: 5}.value }
292
+ * ```
312
293
  *
313
294
  * into this:
314
295
  *
296
+ * ```yaml
315
297
  * - assign1:
316
298
  * assign:
317
299
  * - __temp0:
318
- * value: 5
300
+ * value: 5
319
301
  * - return1:
320
302
  * return: ${__temp0.value}
303
+ * ```
321
304
  */
322
- function mapLiteralsAsAssignSteps(steps) {
323
- const transformNestedMapsInExpressions = transformStepExpressions(transformNestedMaps);
324
- return R.chain(transformNestedMapsInExpressions, steps);
305
+ function mapLiteralsAsAssigns(generateTempName) {
306
+ return (statement) => {
307
+ return expandExpressionToStatements((ex) => transformNestedMaps(generateTempName, ex), statement);
308
+ };
325
309
  }
326
- function transformNestedMaps(ex) {
327
- const generateTemporaryVariableName = createTempVariableGenerator();
328
- const { transformedExpression, tempVariables } = extractNestedMaps(ex, generateTemporaryVariableName, 0);
329
- const assignments = tempVariables.length > 0 ? [new AssignStepAST(tempVariables)] : [];
310
+ function transformNestedMaps(generateTempName, ex) {
311
+ const { transformedExpression, tempVariables } = extractNestedMaps(ex, generateTempName, 0);
312
+ const assignments = tempVariables.length > 0 ? [new AssignStatement(tempVariables)] : [];
330
313
  return [assignments, transformedExpression];
331
314
  }
332
315
  function extractNestedMaps(ex, generateName, nestingLevel) {
333
- switch (ex.expressionType) {
334
- case 'primitive':
335
- return extractNestedMapPrimitive(ex.value, generateName, nestingLevel);
336
- case 'binary':
337
- return extractNestedMapBinary(ex, generateName, nestingLevel);
316
+ switch (ex.tag) {
317
+ case 'string':
318
+ case 'number':
319
+ case 'boolean':
320
+ case 'null':
338
321
  case 'variableReference':
339
322
  return { transformedExpression: ex, tempVariables: [] };
323
+ case 'list':
324
+ return extractMapsInList(ex, generateName, nestingLevel);
325
+ case 'map':
326
+ return extractMapsInMap(ex, generateName, nestingLevel);
327
+ case 'binary':
328
+ return extractNestedMapBinary(ex, generateName, nestingLevel);
340
329
  case 'functionInvocation':
341
330
  return extractNestedMapFunctionInvocation(ex, generateName, nestingLevel);
342
331
  case 'member':
@@ -345,64 +334,24 @@ function extractNestedMaps(ex, generateName, nestingLevel) {
345
334
  return extractNestedMapUnary(ex, generateName, nestingLevel);
346
335
  }
347
336
  }
348
- function extractNestedMapPrimitive(primitiveEx, generateName, nestingLevel) {
349
- const { transformed, tempVariables } = extractNestedMapPrimitiveRecursive(primitiveEx, generateName, nestingLevel);
350
- return {
351
- transformedExpression: asExpression(transformed),
352
- tempVariables,
353
- };
354
- }
355
- function extractNestedMapPrimitiveRecursive(primitiveEx, generateName, nestingLevel) {
356
- if (typeof primitiveEx === 'string' ||
357
- typeof primitiveEx === 'number' ||
358
- typeof primitiveEx === 'boolean' ||
359
- primitiveEx === null) {
360
- return {
361
- transformed: primitiveEx,
362
- tempVariables: [],
363
- };
364
- }
365
- else if (Array.isArray(primitiveEx)) {
366
- return extractMapsInList(primitiveEx, generateName, nestingLevel);
367
- }
368
- else {
369
- return extractMapsInMap(primitiveEx, generateName, nestingLevel);
370
- }
371
- }
372
- function extractMapsInList(primitiveEx, generateName, nestingLevel) {
373
- const { tempVariables, elements } = primitiveEx.reduce((acc, val) => {
374
- if (isExpression(val)) {
375
- const { transformedExpression, tempVariables: temps } = extractNestedMaps(val, generateName, nestingLevel);
376
- acc.tempVariables.push(...temps);
377
- acc.elements.push(transformedExpression);
378
- }
379
- else {
380
- const { transformed, tempVariables: temps } = extractNestedMapPrimitiveRecursive(val, generateName, nestingLevel);
381
- acc.tempVariables.push(...temps);
382
- acc.elements.push(transformed);
383
- }
384
- return acc;
385
- }, {
386
- tempVariables: [],
387
- elements: [],
337
+ function extractMapsInList(list, generateName, nestingLevel) {
338
+ const tempVariables = [];
339
+ const elements = [];
340
+ list.value.forEach((val) => {
341
+ const { transformedExpression, tempVariables: temps } = extractNestedMaps(val, generateName, nestingLevel);
342
+ tempVariables.push(...temps);
343
+ elements.push(transformedExpression);
388
344
  });
389
345
  return {
390
346
  tempVariables,
391
- transformed: elements,
347
+ transformedExpression: listEx(elements),
392
348
  };
393
349
  }
394
- function extractMapsInMap(primitiveEx, generateName, nestingLevel) {
395
- const { tempVariables, properties } = Object.entries(primitiveEx).reduce((acc, [key, val]) => {
396
- if (isExpression(val)) {
397
- const { transformedExpression, tempVariables: temps } = extractNestedMaps(val, generateName, 0);
398
- acc.tempVariables.push(...temps);
399
- acc.properties[key] = transformedExpression;
400
- }
401
- else {
402
- const { transformed, tempVariables: temps } = extractNestedMapPrimitiveRecursive(val, generateName, 0);
403
- acc.tempVariables.push(...temps);
404
- acc.properties[key] = transformed;
405
- }
350
+ function extractMapsInMap(map, generateName, nestingLevel) {
351
+ const { tempVariables, properties } = Object.entries(map.value).reduce((acc, [key, val]) => {
352
+ const { transformedExpression, tempVariables: temps } = extractNestedMaps(val, generateName, 0);
353
+ acc.tempVariables.push(...temps);
354
+ acc.properties[key] = transformedExpression;
406
355
  return acc;
407
356
  }, {
408
357
  tempVariables: [],
@@ -410,17 +359,17 @@ function extractMapsInMap(primitiveEx, generateName, nestingLevel) {
410
359
  });
411
360
  let newValue;
412
361
  if (nestingLevel === 0) {
413
- newValue = properties;
362
+ newValue = mapEx(properties);
414
363
  }
415
364
  else {
416
- newValue = new VariableReferenceExpression(generateName());
365
+ newValue = variableReferenceEx(generateName());
417
366
  tempVariables.push({
418
367
  name: newValue,
419
- value: new PrimitiveExpression(properties),
368
+ value: mapEx(properties),
420
369
  });
421
370
  }
422
371
  return {
423
- transformed: newValue,
372
+ transformedExpression: newValue,
424
373
  tempVariables,
425
374
  };
426
375
  }
@@ -432,7 +381,7 @@ function extractNestedMapFunctionInvocation(ex, generateName, nestingLevel) {
432
381
  return acc;
433
382
  }, { expressions: [], temps: [] });
434
383
  return {
435
- transformedExpression: new FunctionInvocationExpression(ex.functionName, expressions),
384
+ transformedExpression: functionInvocationEx(ex.functionName, expressions),
436
385
  tempVariables: temps,
437
386
  };
438
387
  }
@@ -440,7 +389,7 @@ function extractNestedMapBinary(ex, generateName, nestingLevel) {
440
389
  const left = extractNestedMaps(ex.left, generateName, nestingLevel + 1);
441
390
  const right = extractNestedMaps(ex.right, generateName, nestingLevel + 1);
442
391
  return {
443
- transformedExpression: new BinaryExpression(left.transformedExpression, ex.binaryOperator, right.transformedExpression),
392
+ transformedExpression: binaryEx(left.transformedExpression, ex.binaryOperator, right.transformedExpression),
444
393
  tempVariables: left.tempVariables.concat(right.tempVariables),
445
394
  };
446
395
  }
@@ -448,31 +397,27 @@ function extractNestedMapMember(ex, generateName, nestingLevel) {
448
397
  const obj = extractNestedMaps(ex.object, generateName, nestingLevel + 1);
449
398
  const pr = extractNestedMaps(ex.property, generateName, nestingLevel + 1);
450
399
  return {
451
- transformedExpression: new MemberExpression(obj.transformedExpression, pr.transformedExpression, ex.computed),
400
+ transformedExpression: memberEx(obj.transformedExpression, pr.transformedExpression, ex.computed),
452
401
  tempVariables: obj.tempVariables.concat(pr.tempVariables),
453
402
  };
454
403
  }
455
404
  function extractNestedMapUnary(ex, generateName, nestingLevel) {
456
405
  const { transformedExpression, tempVariables } = extractNestedMaps(ex.value, generateName, nestingLevel);
457
406
  return {
458
- transformedExpression: new UnaryExpression(ex.operator, transformedExpression),
407
+ transformedExpression: unaryEx(ex.operator, transformedExpression),
459
408
  tempVariables,
460
409
  };
461
410
  }
462
411
  /**
463
412
  * Replace `Array.isArray(x)` with `get_type(x) == "list"`
464
413
  */
465
- function intrinsicFunctionImplementation(steps) {
466
- const tr = transformStepExpressions((ex) => [
467
- [],
468
- transformExpression(replaceIsArray, ex),
469
- ]);
470
- return R.chain(tr, steps);
471
- }
414
+ const intrinsicFunctionImplementation = expandExpressionToStatements((ex) => [
415
+ [],
416
+ transformExpression(replaceIsArray, ex),
417
+ ]);
472
418
  function replaceIsArray(ex) {
473
- if (ex.expressionType === 'functionInvocation' &&
474
- ex.functionName === 'Array.isArray') {
475
- return new BinaryExpression(new FunctionInvocationExpression('get_type', ex.arguments), '==', new PrimitiveExpression('list'));
419
+ if (ex.tag === 'functionInvocation' && ex.functionName === 'Array.isArray') {
420
+ return binaryEx(functionInvocationEx('get_type', ex.arguments), '==', stringEx('list'));
476
421
  }
477
422
  else {
478
423
  return ex;