ts2workflows 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +36 -9
- package/dist/ast/expressions.d.ts +37 -41
- package/dist/ast/expressions.d.ts.map +1 -1
- package/dist/ast/expressions.js +124 -255
- package/dist/ast/statements.d.ts +132 -0
- package/dist/ast/statements.d.ts.map +1 -0
- package/dist/ast/statements.js +197 -0
- package/dist/ast/steps.d.ts +82 -194
- package/dist/ast/steps.d.ts.map +1 -1
- package/dist/ast/steps.js +862 -521
- package/dist/ast/workflows.d.ts +14 -12
- package/dist/ast/workflows.d.ts.map +1 -1
- package/dist/ast/workflows.js +20 -35
- package/dist/cli.js +45 -32
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/transpiler/index.d.ts +19 -1
- package/dist/transpiler/index.d.ts.map +1 -1
- package/dist/transpiler/index.js +158 -31
- package/dist/transpiler/linker.d.ts +3 -0
- package/dist/transpiler/linker.d.ts.map +1 -0
- package/dist/transpiler/linker.js +41 -0
- package/dist/transpiler/parseexpressions.d.ts +11 -0
- package/dist/transpiler/parseexpressions.d.ts.map +1 -0
- package/dist/transpiler/{expressions.js → parseexpressions.js} +90 -103
- package/dist/transpiler/parsestatement.d.ts +7 -0
- package/dist/transpiler/parsestatement.d.ts.map +1 -0
- package/dist/transpiler/parsestatement.js +862 -0
- package/dist/transpiler/stepnames.d.ts +3 -0
- package/dist/transpiler/stepnames.d.ts.map +1 -0
- package/dist/transpiler/stepnames.js +17 -0
- package/dist/transpiler/transformations.d.ts +3 -19
- package/dist/transpiler/transformations.d.ts.map +1 -1
- package/dist/transpiler/transformations.js +267 -322
- package/language_reference.md +53 -17
- package/package.json +15 -11
- package/types/workflowslib.d.ts +58 -74
- package/dist/ast/stepnames.d.ts +0 -9
- package/dist/ast/stepnames.d.ts.map +0 -1
- package/dist/ast/stepnames.js +0 -280
- package/dist/ast/validation.d.ts +0 -20
- package/dist/ast/validation.d.ts.map +0 -1
- package/dist/ast/validation.js +0 -214
- package/dist/transpiler/expressions.d.ts +0 -11
- package/dist/transpiler/expressions.d.ts.map +0 -1
- package/dist/transpiler/statements.d.ts +0 -10
- package/dist/transpiler/statements.d.ts.map +0 -1
- package/dist/transpiler/statements.js +0 -1100
- package/dist/utils.d.ts +0 -9
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -13
|
@@ -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 {
|
|
5
|
-
import {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
15
|
+
* Merge consecutive assign statements into one assign statement
|
|
16
16
|
*
|
|
17
|
-
* An assign
|
|
17
|
+
* An assign is allowed to contain up to 50 assignments.
|
|
18
18
|
*/
|
|
19
|
-
function
|
|
20
|
-
return
|
|
19
|
+
function mergeAssigns(statements) {
|
|
20
|
+
return statements.reduce((acc, current) => {
|
|
21
21
|
const prev = acc.length > 0 ? acc[acc.length - 1] : null;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
41
|
+
if (prevAssignments && currentAssignments) {
|
|
42
|
+
let merged = new AssignStatement(prevAssignments.concat(currentAssignments));
|
|
43
|
+
if (label) {
|
|
44
|
+
merged = new LabelledStatement(label, [merged]);
|
|
130
45
|
}
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
50
|
+
acc.push(current);
|
|
140
51
|
}
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
56
|
+
* Transform expressions in a statement by applying transform.
|
|
151
57
|
*
|
|
152
|
-
* Returns an array of
|
|
153
|
-
* additional
|
|
154
|
-
* transformation might extract blocking call expressions into
|
|
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
|
|
157
|
-
switch (
|
|
63
|
+
const expandExpressionToStatements = R.curry(function (transform, statement) {
|
|
64
|
+
switch (statement.tag) {
|
|
158
65
|
case 'assign':
|
|
159
|
-
return
|
|
160
|
-
case '
|
|
161
|
-
return
|
|
66
|
+
return expandAssign(transform, statement);
|
|
67
|
+
case 'function-invocation':
|
|
68
|
+
return expandFunctionInvocation(transform, statement);
|
|
162
69
|
case 'for':
|
|
163
|
-
return
|
|
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
|
|
76
|
+
return expandRaise(transform, statement);
|
|
166
77
|
case 'return':
|
|
167
|
-
return
|
|
78
|
+
return expanrdReturn(transform, statement);
|
|
168
79
|
case 'switch':
|
|
169
|
-
return
|
|
170
|
-
case '
|
|
171
|
-
case '
|
|
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 '
|
|
88
|
+
case 'parallel-for':
|
|
174
89
|
case 'try':
|
|
175
|
-
|
|
176
|
-
return [step];
|
|
90
|
+
return [statement];
|
|
177
91
|
}
|
|
178
92
|
});
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
201
|
-
if (
|
|
202
|
-
const
|
|
109
|
+
function expandFunctionInvocation(transform, statement) {
|
|
110
|
+
if (statement.args) {
|
|
111
|
+
const newStatements = [];
|
|
203
112
|
const newArgs = R.map((ex) => {
|
|
204
|
-
const [
|
|
205
|
-
|
|
113
|
+
const [st2, ex2] = transform(ex);
|
|
114
|
+
newStatements.push(...st2);
|
|
206
115
|
return ex2;
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
return
|
|
116
|
+
}, statement.args);
|
|
117
|
+
newStatements.push({ ...statement, args: newArgs });
|
|
118
|
+
return newStatements;
|
|
210
119
|
}
|
|
211
120
|
else {
|
|
212
|
-
return [
|
|
121
|
+
return [statement];
|
|
213
122
|
}
|
|
214
123
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
137
|
+
const [st1, ex1] = transform(statement.rangeStart);
|
|
138
|
+
res.push(...st1);
|
|
139
|
+
newRangeStart = ex1;
|
|
223
140
|
}
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
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
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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 [
|
|
177
|
+
return [statement];
|
|
243
178
|
}
|
|
244
179
|
}
|
|
245
|
-
function
|
|
246
|
-
const
|
|
247
|
-
const newBranches =
|
|
248
|
-
const [
|
|
249
|
-
|
|
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
|
-
|
|
253
|
-
next: cond.next,
|
|
187
|
+
body: branch.body,
|
|
254
188
|
};
|
|
255
189
|
});
|
|
256
|
-
|
|
257
|
-
return
|
|
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
|
|
257
|
+
* Transform leaf expressions first.
|
|
262
258
|
*/
|
|
263
259
|
function transformExpression(transform, ex) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
case '
|
|
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
|
|
275
|
+
return transform(functionInvocationEx(ex.functionName, ex.arguments.map(nestedTr)));
|
|
285
276
|
case 'member':
|
|
286
|
-
return
|
|
277
|
+
return transform(memberEx(nestedTr(ex.object), nestedTr(ex.property), ex.computed));
|
|
287
278
|
case 'unary':
|
|
288
|
-
return
|
|
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
|
|
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
|
-
*
|
|
300
|
+
* value: 5
|
|
319
301
|
* - return1:
|
|
320
302
|
* return: ${__temp0.value}
|
|
303
|
+
* ```
|
|
321
304
|
*/
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
|
|
305
|
+
function mapLiteralsAsAssigns(generateTempName) {
|
|
306
|
+
return (statement) => {
|
|
307
|
+
return expandExpressionToStatements((ex) => transformNestedMaps(generateTempName, ex), statement);
|
|
308
|
+
};
|
|
325
309
|
}
|
|
326
|
-
function transformNestedMaps(ex) {
|
|
327
|
-
const
|
|
328
|
-
const
|
|
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.
|
|
334
|
-
case '
|
|
335
|
-
|
|
336
|
-
case '
|
|
337
|
-
|
|
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
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
347
|
+
transformedExpression: listEx(elements),
|
|
392
348
|
};
|
|
393
349
|
}
|
|
394
|
-
function extractMapsInMap(
|
|
395
|
-
const { tempVariables, properties } = Object.entries(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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 =
|
|
365
|
+
newValue = variableReferenceEx(generateName());
|
|
417
366
|
tempVariables.push({
|
|
418
367
|
name: newValue,
|
|
419
|
-
value:
|
|
368
|
+
value: mapEx(properties),
|
|
420
369
|
});
|
|
421
370
|
}
|
|
422
371
|
return {
|
|
423
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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.
|
|
474
|
-
ex.
|
|
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;
|