ts2workflows 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +82 -0
- package/dist/ast/expressions.d.ts +57 -0
- package/dist/ast/expressions.d.ts.map +1 -0
- package/dist/ast/expressions.js +300 -0
- package/dist/ast/stepnames.d.ts +9 -0
- package/dist/ast/stepnames.d.ts.map +1 -0
- package/dist/ast/stepnames.js +268 -0
- package/dist/ast/steps.d.ts +176 -0
- package/dist/ast/steps.d.ts.map +1 -0
- package/dist/ast/steps.js +534 -0
- package/dist/ast/validation.d.ts +20 -0
- package/dist/ast/validation.d.ts.map +1 -0
- package/dist/ast/validation.js +214 -0
- package/dist/ast/workflows.d.ts +28 -0
- package/dist/ast/workflows.d.ts.map +1 -0
- package/dist/ast/workflows.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/transpiler/asserts.d.ts +7 -0
- package/dist/transpiler/asserts.d.ts.map +1 -0
- package/dist/transpiler/asserts.js +11 -0
- package/dist/transpiler/expressions.d.ts +6 -0
- package/dist/transpiler/expressions.d.ts.map +1 -0
- package/dist/transpiler/expressions.js +223 -0
- package/dist/transpiler/generated/functionMetadata.d.ts +2 -0
- package/dist/transpiler/generated/functionMetadata.d.ts.map +1 -0
- package/dist/transpiler/generated/functionMetadata.js +324 -0
- package/dist/transpiler/index.d.ts +2 -0
- package/dist/transpiler/index.d.ts.map +1 -0
- package/dist/transpiler/index.js +74 -0
- package/dist/transpiler/statements.d.ts +7 -0
- package/dist/transpiler/statements.d.ts.map +1 -0
- package/dist/transpiler/statements.js +533 -0
- package/dist/transpiler/transformations.d.ts +28 -0
- package/dist/transpiler/transformations.d.ts.map +1 -0
- package/dist/transpiler/transformations.js +461 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +3 -0
- package/language_reference.md +771 -0
- package/package.json +62 -0
- package/types/workflowslib.d.ts +714 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { AssignStepAST, CallStepAST, ForStepAST, RaiseStepAST, ReturnStepAST, SwitchStepAST, TryStepAST, } from '../ast/steps.js';
|
|
2
|
+
import { InternalTranspilingError } from '../errors.js';
|
|
3
|
+
import { isRecord } from '../utils.js';
|
|
4
|
+
import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, UnaryExpression, VariableReferenceExpression, expressionToLiteralValueOrLiteralExpression, isExpression, isFullyQualifiedName, isLiteral, } from '../ast/expressions.js';
|
|
5
|
+
import { blockingFunctions } from './generated/functionMetadata.js';
|
|
6
|
+
/**
|
|
7
|
+
* Performs various transformations on the AST.
|
|
8
|
+
*
|
|
9
|
+
* This flat list of steps and does not recurse into nested steps. This gets
|
|
10
|
+
* called on each nesting level separately.
|
|
11
|
+
*/
|
|
12
|
+
export function transformAST(steps) {
|
|
13
|
+
return blockingCallsAsCallSteps(flattenPlainNextConditions(combineRetryBlocksToTry(mergeAssignSteps(mapLiteralsAsAssignSteps(steps)))));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Merge consecutive assign steps into one assign step
|
|
17
|
+
*
|
|
18
|
+
* An assign step can contain up to 50 assignments.
|
|
19
|
+
*/
|
|
20
|
+
function mergeAssignSteps(steps) {
|
|
21
|
+
return steps.reduce((acc, current) => {
|
|
22
|
+
const prev = acc.length > 0 ? acc[acc.length - 1] : null;
|
|
23
|
+
if (current.tag === 'assign' &&
|
|
24
|
+
prev?.tag === 'assign' &&
|
|
25
|
+
prev.assignments.length < 50) {
|
|
26
|
+
const merged = new AssignStepAST(prev.assignments.concat(current.assignments));
|
|
27
|
+
acc.pop();
|
|
28
|
+
acc.push(merged);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
acc.push(current);
|
|
32
|
+
}
|
|
33
|
+
return acc;
|
|
34
|
+
}, []);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Transform a retry_policy call step to a retry block in a preceeding try step
|
|
38
|
+
*/
|
|
39
|
+
function combineRetryBlocksToTry(steps) {
|
|
40
|
+
return steps.reduce((acc, current) => {
|
|
41
|
+
const prev = acc.length > 0 ? acc[acc.length - 1] : null;
|
|
42
|
+
if (current.tag === 'call' && current.call === 'retry_policy') {
|
|
43
|
+
if (prev?.tag === 'try') {
|
|
44
|
+
if (prev.retryPolicy) {
|
|
45
|
+
throw new InternalTranspilingError('Retry policy already assigned!');
|
|
46
|
+
}
|
|
47
|
+
let retryPolicy = undefined;
|
|
48
|
+
const retryParameters = current.args;
|
|
49
|
+
if (retryParameters) {
|
|
50
|
+
const retryPolicyEx = retryParameters.policy;
|
|
51
|
+
if (retryPolicyEx && isFullyQualifiedName(retryPolicyEx)) {
|
|
52
|
+
retryPolicy = retryPolicyEx.toString();
|
|
53
|
+
}
|
|
54
|
+
if (!retryPolicy) {
|
|
55
|
+
let predicate = '';
|
|
56
|
+
const predicateEx = retryParameters.predicate;
|
|
57
|
+
if (predicateEx) {
|
|
58
|
+
if (isFullyQualifiedName(predicateEx)) {
|
|
59
|
+
predicate = predicateEx.toString();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new InternalTranspilingError('"predicate" must be a function name');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const maxRetries = parseRetryPolicyNumber(retryParameters, 'max_retries');
|
|
66
|
+
let initialDelay = 1;
|
|
67
|
+
let maxDelay = 1;
|
|
68
|
+
let multiplier = 1;
|
|
69
|
+
const backoffEx = retryParameters.backoff;
|
|
70
|
+
if (backoffEx &&
|
|
71
|
+
isLiteral(backoffEx) &&
|
|
72
|
+
backoffEx.expressionType === 'primitive') {
|
|
73
|
+
const backoffLit = backoffEx.value;
|
|
74
|
+
if (isRecord(backoffLit)) {
|
|
75
|
+
initialDelay = parseRetryPolicyNumber(backoffLit, 'initial_delay');
|
|
76
|
+
maxDelay = parseRetryPolicyNumber(backoffLit, 'max_delay');
|
|
77
|
+
multiplier = parseRetryPolicyNumber(backoffLit, 'multiplier');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
retryPolicy = {
|
|
81
|
+
predicate,
|
|
82
|
+
maxRetries,
|
|
83
|
+
backoff: {
|
|
84
|
+
initialDelay,
|
|
85
|
+
maxDelay,
|
|
86
|
+
multiplier,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const tryWithRetry = new TryStepAST(prev.trySteps, prev.exceptSteps, retryPolicy, prev.errorMap);
|
|
92
|
+
acc.pop();
|
|
93
|
+
acc.push(tryWithRetry);
|
|
94
|
+
}
|
|
95
|
+
// If prev is not a try step, "retry_policy" is ignored. Should print a warning.
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
acc.push(current);
|
|
99
|
+
}
|
|
100
|
+
return acc;
|
|
101
|
+
}, []);
|
|
102
|
+
}
|
|
103
|
+
function parseRetryPolicyNumber(record, keyName) {
|
|
104
|
+
let primitiveValue;
|
|
105
|
+
const primitiveOrExpression = record[keyName];
|
|
106
|
+
if (primitiveOrExpression && isExpression(primitiveOrExpression)) {
|
|
107
|
+
if (isLiteral(primitiveOrExpression)) {
|
|
108
|
+
primitiveValue = expressionToLiteralValueOrLiteralExpression(primitiveOrExpression);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
throw new InternalTranspilingError(`Support for non-literal "${keyName}" values not yet implemented`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (primitiveOrExpression) {
|
|
115
|
+
primitiveValue = primitiveOrExpression;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
throw new InternalTranspilingError(`"${keyName}" expected`);
|
|
119
|
+
}
|
|
120
|
+
if (typeof primitiveValue !== 'number') {
|
|
121
|
+
throw new InternalTranspilingError(`"${keyName}" must be a number`);
|
|
122
|
+
}
|
|
123
|
+
return primitiveValue;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Flatten switch conditions that contain a single next step.
|
|
127
|
+
*
|
|
128
|
+
* For example, transforms this:
|
|
129
|
+
*
|
|
130
|
+
* switch:
|
|
131
|
+
* - condition: ${x > 0}
|
|
132
|
+
* steps:
|
|
133
|
+
* - next1:
|
|
134
|
+
* steps:
|
|
135
|
+
* next: target1
|
|
136
|
+
*
|
|
137
|
+
* into this:
|
|
138
|
+
*
|
|
139
|
+
* switch:
|
|
140
|
+
* - condition: ${x > 0}
|
|
141
|
+
* next: target1
|
|
142
|
+
*/
|
|
143
|
+
export function flattenPlainNextConditions(steps) {
|
|
144
|
+
return steps.map((step) => (step.tag === 'switch' ? flattenNext(step) : step));
|
|
145
|
+
}
|
|
146
|
+
function flattenNext(step) {
|
|
147
|
+
const transformedBranches = step.branches.map((cond) => {
|
|
148
|
+
if (!cond.next && cond.steps.length === 1 && cond.steps[0].tag === 'next') {
|
|
149
|
+
const nextStep = cond.steps[0];
|
|
150
|
+
return {
|
|
151
|
+
condition: cond.condition,
|
|
152
|
+
steps: [],
|
|
153
|
+
next: nextStep.target,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
return cond;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
return new SwitchStepAST(transformedBranches);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Search for blocking calls in expressions and replace them with call step + variable.
|
|
164
|
+
*
|
|
165
|
+
* The Workflows runtime requires that blocking calls must be executed by call steps.
|
|
166
|
+
*
|
|
167
|
+
* For example, transforms this:
|
|
168
|
+
*
|
|
169
|
+
* - return1:
|
|
170
|
+
* return: ${http.get("https://example.com")}
|
|
171
|
+
*
|
|
172
|
+
* into this:
|
|
173
|
+
*
|
|
174
|
+
* - call_http_get_1:
|
|
175
|
+
* call: http.get
|
|
176
|
+
* args:
|
|
177
|
+
* url: https://example.com
|
|
178
|
+
* result: __temp0
|
|
179
|
+
* - return1:
|
|
180
|
+
* return: ${__temp0}
|
|
181
|
+
*/
|
|
182
|
+
function blockingCallsAsCallSteps(steps) {
|
|
183
|
+
const transformer = (ex) => {
|
|
184
|
+
const generateTemporaryVariableName = createTempVariableGenerator();
|
|
185
|
+
const { transformedExpression, callSteps } = replaceBlockingCalls(ex, generateTemporaryVariableName);
|
|
186
|
+
return [callSteps, transformedExpression];
|
|
187
|
+
};
|
|
188
|
+
return steps.reduce((acc, current) => {
|
|
189
|
+
const transformedSteps = transformStepExpressions(current, transformer);
|
|
190
|
+
acc.push(...transformedSteps);
|
|
191
|
+
return acc;
|
|
192
|
+
}, []);
|
|
193
|
+
}
|
|
194
|
+
function createTempVariableGenerator() {
|
|
195
|
+
let i = 0;
|
|
196
|
+
const generator = () => `__temp${i++}`;
|
|
197
|
+
return generator;
|
|
198
|
+
}
|
|
199
|
+
const Unmodified = Symbol();
|
|
200
|
+
function replaceBlockingCalls(expression, generateName) {
|
|
201
|
+
function replaceBlockingFunctionInvocations(ex) {
|
|
202
|
+
if (ex.expressionType === 'functionInvocation') {
|
|
203
|
+
const callStepsForArguments = [];
|
|
204
|
+
const replacedArguments = ex.arguments.map((ex) => {
|
|
205
|
+
const replaced = replaceBlockingCalls(ex, generateName);
|
|
206
|
+
callStepsForArguments.push(...replaced.callSteps);
|
|
207
|
+
return replaced.transformedExpression;
|
|
208
|
+
});
|
|
209
|
+
const blockingCallArgumentNames = blockingFunctions.get(ex.functionName);
|
|
210
|
+
if (blockingCallArgumentNames) {
|
|
211
|
+
if (replacedArguments.length > blockingCallArgumentNames.length) {
|
|
212
|
+
throw new InternalTranspilingError('FunctionInvocationTerm has more arguments than metadata allows!');
|
|
213
|
+
}
|
|
214
|
+
const nameAndValue = replacedArguments.map((val, i) => [blockingCallArgumentNames[i], val]);
|
|
215
|
+
const args = Object.fromEntries(nameAndValue);
|
|
216
|
+
const tempCallResultVariable = generateName();
|
|
217
|
+
callSteps.push(...callStepsForArguments);
|
|
218
|
+
callSteps.push(new CallStepAST(ex.functionName, args, tempCallResultVariable));
|
|
219
|
+
// replace function invocation with a reference to the temporary variable
|
|
220
|
+
return new VariableReferenceExpression(tempCallResultVariable);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
return Unmodified;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return Unmodified;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const callSteps = [];
|
|
231
|
+
return {
|
|
232
|
+
transformedExpression: transformExpression(expression, replaceBlockingFunctionInvocations),
|
|
233
|
+
callSteps,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Transform expressions in a step by applying transform.
|
|
238
|
+
*
|
|
239
|
+
* Returns an array of steps. The array includes the transformed step and possibly
|
|
240
|
+
* additional steps constructed during the transformation. For example, a
|
|
241
|
+
* transformation might extract blocking call expressions into call steps.
|
|
242
|
+
*/
|
|
243
|
+
function transformStepExpressions(step, transform) {
|
|
244
|
+
switch (step.tag) {
|
|
245
|
+
case 'assign':
|
|
246
|
+
return transformExpressionsAssign(step, transform);
|
|
247
|
+
case 'call':
|
|
248
|
+
return transformExpressionsCall(step, transform);
|
|
249
|
+
case 'for':
|
|
250
|
+
return transformExpressionsFor(step, transform);
|
|
251
|
+
case 'raise':
|
|
252
|
+
return transformExpressionsRaise(step, transform);
|
|
253
|
+
case 'return':
|
|
254
|
+
return transformExpressionsReturn(step, transform);
|
|
255
|
+
case 'switch':
|
|
256
|
+
return transformExpressionsSwitch(step, transform);
|
|
257
|
+
case 'next':
|
|
258
|
+
case 'parallel':
|
|
259
|
+
case 'steps':
|
|
260
|
+
case 'try':
|
|
261
|
+
case 'jumptarget':
|
|
262
|
+
return [step];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function transformExpressionsAssign(step, transform) {
|
|
266
|
+
if (step.assignments) {
|
|
267
|
+
const newSteps = [];
|
|
268
|
+
const newAssignments = step.assignments.map(([name, ex]) => {
|
|
269
|
+
const [steps2, ex2] = transform(ex);
|
|
270
|
+
newSteps.push(...steps2);
|
|
271
|
+
return [name, ex2];
|
|
272
|
+
});
|
|
273
|
+
newSteps.push(new AssignStepAST(newAssignments, step.label));
|
|
274
|
+
return newSteps;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
return [step];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function transformExpressionsCall(step, transform) {
|
|
281
|
+
if (step.args) {
|
|
282
|
+
const newSteps = [];
|
|
283
|
+
const newArgs = Object.fromEntries(Object.entries(step.args).map(([name, ex]) => {
|
|
284
|
+
const [steps2, ex2] = transform(ex);
|
|
285
|
+
newSteps.push(...steps2);
|
|
286
|
+
return [name, ex2];
|
|
287
|
+
}));
|
|
288
|
+
newSteps.push(new CallStepAST(step.call, newArgs, step.result, step.label));
|
|
289
|
+
return newSteps;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
return [step];
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function transformExpressionsFor(step, transform) {
|
|
296
|
+
if (step.listExpression) {
|
|
297
|
+
const [newSteps, newListExpression] = transform(step.listExpression);
|
|
298
|
+
newSteps.push(new ForStepAST(step.steps, step.loopVariableName, newListExpression, step.indexVariableName, step.rangeStart, step.rangeEnd, step.label));
|
|
299
|
+
return newSteps;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
return [step];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function transformExpressionsRaise(step, transform) {
|
|
306
|
+
if (step.value) {
|
|
307
|
+
const [newSteps, newEx] = transform(step.value);
|
|
308
|
+
newSteps.push(new RaiseStepAST(newEx, step.label));
|
|
309
|
+
return newSteps;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
return [step];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function transformExpressionsReturn(step, transform) {
|
|
316
|
+
if (step.value) {
|
|
317
|
+
const [newSteps, newEx] = transform(step.value);
|
|
318
|
+
newSteps.push(new ReturnStepAST(newEx, step.label));
|
|
319
|
+
return newSteps;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
return [step];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function transformExpressionsSwitch(step, transform) {
|
|
326
|
+
const newSteps = [];
|
|
327
|
+
const newBranches = step.branches.map((cond) => {
|
|
328
|
+
const [steps2, ex2] = transform(cond.condition);
|
|
329
|
+
newSteps.push(...steps2);
|
|
330
|
+
return {
|
|
331
|
+
condition: ex2,
|
|
332
|
+
steps: cond.steps,
|
|
333
|
+
next: cond.next,
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
newSteps.push(new SwitchStepAST(newBranches, step.label));
|
|
337
|
+
return newSteps;
|
|
338
|
+
}
|
|
339
|
+
function transformExpression(ex, transformer) {
|
|
340
|
+
const transformed = transformer(ex);
|
|
341
|
+
if (transformed !== Unmodified) {
|
|
342
|
+
// Use the transformed version of this term
|
|
343
|
+
return transformed;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// Otherwise, recurse into the nested expression
|
|
347
|
+
switch (ex.expressionType) {
|
|
348
|
+
case 'primitive':
|
|
349
|
+
case 'variableReference':
|
|
350
|
+
return ex;
|
|
351
|
+
case 'binary':
|
|
352
|
+
return transformBinaryExpression(ex, transformer);
|
|
353
|
+
case 'functionInvocation':
|
|
354
|
+
return transformFunctionInvocationExpression(ex, transformer);
|
|
355
|
+
case 'member':
|
|
356
|
+
return new MemberExpression(transformExpression(ex.object, transformer), transformExpression(ex.property, transformer), ex.computed);
|
|
357
|
+
case 'unary':
|
|
358
|
+
return new UnaryExpression(ex.operator, transformExpression(ex.value, transformer));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function transformBinaryExpression(ex, transformer) {
|
|
363
|
+
// Transform left first to keep the correct order of execution of sub-expressions
|
|
364
|
+
const newLeft = transformExpression(ex.left, transformer);
|
|
365
|
+
const newRight = transformExpression(ex.right, transformer);
|
|
366
|
+
return new BinaryExpression(newLeft, ex.binaryOperator, newRight);
|
|
367
|
+
}
|
|
368
|
+
function transformFunctionInvocationExpression(ex, transformer) {
|
|
369
|
+
const newArguments = ex.arguments.map((x) => transformExpression(x, transformer));
|
|
370
|
+
return new FunctionInvocationExpression(ex.functionName, newArguments);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Search for map literals in expressions and replace them with assign step + variable.
|
|
374
|
+
*
|
|
375
|
+
* Workflows does not support a map literal in expressions.
|
|
376
|
+
*
|
|
377
|
+
* For example, transforms this:
|
|
378
|
+
*
|
|
379
|
+
* - return1:
|
|
380
|
+
* return: ${ {value: 5}.value }
|
|
381
|
+
*
|
|
382
|
+
* into this:
|
|
383
|
+
*
|
|
384
|
+
* - assign1:
|
|
385
|
+
* assign:
|
|
386
|
+
* - __temp0:
|
|
387
|
+
* value: 5
|
|
388
|
+
* - return1:
|
|
389
|
+
* return: ${__temp0.value}
|
|
390
|
+
*/
|
|
391
|
+
function mapLiteralsAsAssignSteps(steps) {
|
|
392
|
+
function transformer(ex) {
|
|
393
|
+
const generateTemporaryVariableName = createTempVariableGenerator();
|
|
394
|
+
const { transformedExpression, assignSteps } = replaceMapLiterals(ex, generateTemporaryVariableName);
|
|
395
|
+
return [assignSteps, transformedExpression];
|
|
396
|
+
}
|
|
397
|
+
return steps.reduce((acc, current) => {
|
|
398
|
+
let needsTransformation = true;
|
|
399
|
+
// These steps are allowed to contain map literals if the map is the
|
|
400
|
+
// main expression
|
|
401
|
+
if (current.tag === 'assign') {
|
|
402
|
+
// This does the transformation a bit too eagerly: If any of the
|
|
403
|
+
// assignments need the map literal extraction, it is done on all of
|
|
404
|
+
// the variables.
|
|
405
|
+
needsTransformation = !current.assignments.every(([, value]) => {
|
|
406
|
+
return value.expressionType === 'primitive';
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
else if (current.tag === 'raise') {
|
|
410
|
+
needsTransformation =
|
|
411
|
+
!isLiteral(current.value) && includesMapLiteral(current.value);
|
|
412
|
+
}
|
|
413
|
+
else if (current.tag === 'call') {
|
|
414
|
+
if (current.args) {
|
|
415
|
+
needsTransformation = Object.values(current.args).some((ex) => !isLiteral(ex) && includesMapLiteral(ex));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (needsTransformation) {
|
|
419
|
+
const transformedSteps = transformStepExpressions(current, transformer);
|
|
420
|
+
acc.push(...transformedSteps);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
acc.push(current);
|
|
424
|
+
}
|
|
425
|
+
return acc;
|
|
426
|
+
}, []);
|
|
427
|
+
}
|
|
428
|
+
function includesMapLiteral(ex) {
|
|
429
|
+
switch (ex.expressionType) {
|
|
430
|
+
case 'primitive':
|
|
431
|
+
return isRecord(ex.value);
|
|
432
|
+
case 'binary':
|
|
433
|
+
return includesMapLiteral(ex.left) || includesMapLiteral(ex.right);
|
|
434
|
+
case 'variableReference':
|
|
435
|
+
return false;
|
|
436
|
+
case 'unary':
|
|
437
|
+
return includesMapLiteral(ex.value);
|
|
438
|
+
case 'functionInvocation':
|
|
439
|
+
return ex.arguments.some(includesMapLiteral);
|
|
440
|
+
case 'member':
|
|
441
|
+
return includesMapLiteral(ex.object) || includesMapLiteral(ex.property);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function replaceMapLiterals(expression, generateName) {
|
|
445
|
+
function replace(ex) {
|
|
446
|
+
if (ex.expressionType === 'primitive' && isRecord(ex.value)) {
|
|
447
|
+
const tempVariable = generateName();
|
|
448
|
+
assignSteps.push(new AssignStepAST([[tempVariable, new PrimitiveExpression(ex.value)]]));
|
|
449
|
+
// replace the map literal with a reference to the temporary variable
|
|
450
|
+
return new VariableReferenceExpression(tempVariable);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
return Unmodified;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const assignSteps = [];
|
|
457
|
+
return {
|
|
458
|
+
transformedExpression: transformExpression(expression, replace),
|
|
459
|
+
assignSteps,
|
|
460
|
+
};
|
|
461
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CACtB,MAAM,EAAE,OAAO,GACd,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,OAAO,CAAC,CAExC"}
|
package/dist/utils.js
ADDED