@angular-wave/angular.ts 0.0.50 → 0.0.51
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/README.md +3 -1
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/package.json +1 -1
- package/src/animations/shared.js +2 -1
- package/src/core/parser/ast-type.js +21 -20
- package/src/core/parser/ast.js +34 -35
- package/src/core/parser/interpreter.js +405 -136
- package/src/core/parser/lexer.js +14 -13
- package/src/core/parser/parse.js +31 -45
- package/src/core/parser/parse.spec.js +429 -444
- package/src/core/parser/parser.js +17 -9
- package/src/loader.js +5 -1
- package/src/types.js +0 -13
- package/types/animations/shared.d.ts +1 -1
- package/types/core/parser/ast-type.d.ts +24 -20
- package/types/core/parser/ast.d.ts +13 -14
- package/types/core/parser/interpreter.d.ts +24 -19
- package/types/core/parser/lexer.d.ts +6 -2
- package/types/core/parser/parse.d.ts +44 -38
- package/types/core/parser/parser.d.ts +2 -10
- package/types/loader.d.ts +397 -0
- package/types/types.d.ts +0 -1
- package/src/core/parser/shared.js +0 -234
- package/types/core/parser/shared.d.ts +0 -35
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
assignableAST,
|
|
3
|
-
findConstantAndWatchExpressions,
|
|
4
|
-
getInputs,
|
|
5
|
-
getStringValue,
|
|
6
|
-
plusFn,
|
|
7
|
-
} from "./shared";
|
|
8
|
-
import { forEach, isDefined } from "../../shared/utils";
|
|
1
|
+
import { isDefined } from "../../shared/utils";
|
|
9
2
|
import { ASTType } from "./ast-type";
|
|
10
3
|
|
|
4
|
+
export const PURITY_ABSOLUTE = 1;
|
|
5
|
+
export const PURITY_RELATIVE = 2;
|
|
6
|
+
|
|
11
7
|
export class ASTInterpreter {
|
|
12
8
|
/**
|
|
13
9
|
* @param {function(any):any} $filter
|
|
@@ -19,49 +15,53 @@ export class ASTInterpreter {
|
|
|
19
15
|
/**
|
|
20
16
|
* Compiles the AST into a function.
|
|
21
17
|
* @param {import("./ast").ASTNode} ast - The AST to compile.
|
|
22
|
-
* @returns
|
|
18
|
+
* @returns {import("./parse").CompiledExpression}
|
|
23
19
|
*/
|
|
24
20
|
compile(ast) {
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
let decoratedNode = findConstantAndWatchExpressions(ast, this.$filter);
|
|
22
|
+
/** @type {import("./ast").ASTNode} */
|
|
27
23
|
let assignable;
|
|
24
|
+
/** @type {import("./parse").CompiledExpression} */
|
|
28
25
|
let assign;
|
|
29
|
-
if ((assignable = assignableAST(
|
|
30
|
-
assign =
|
|
26
|
+
if ((assignable = assignableAST(decoratedNode))) {
|
|
27
|
+
assign = /** @type {import("./parse").CompiledExpression} */ (
|
|
28
|
+
this.recurse(assignable)
|
|
29
|
+
);
|
|
31
30
|
}
|
|
32
|
-
const toWatch = getInputs(
|
|
31
|
+
const toWatch = getInputs(decoratedNode.body);
|
|
33
32
|
let inputs;
|
|
34
33
|
if (toWatch) {
|
|
35
34
|
inputs = [];
|
|
36
|
-
|
|
37
|
-
const input =
|
|
35
|
+
for (const [key, watch] of Object.entries(toWatch)) {
|
|
36
|
+
const input = /** @type {import("./parse").CompiledExpression} */ (
|
|
37
|
+
this.recurse(watch)
|
|
38
|
+
);
|
|
38
39
|
input.isPure = watch.isPure;
|
|
39
40
|
watch.input = input;
|
|
40
41
|
inputs.push(input);
|
|
41
42
|
watch.watchId = key;
|
|
42
|
-
}
|
|
43
|
+
}
|
|
43
44
|
}
|
|
44
45
|
const expressions = [];
|
|
45
|
-
|
|
46
|
-
expressions.push(
|
|
46
|
+
decoratedNode.body.forEach((expression) => {
|
|
47
|
+
expressions.push(this.recurse(expression.expression));
|
|
47
48
|
});
|
|
48
49
|
|
|
50
|
+
/** @type {import("./parse").CompiledExpression} */
|
|
49
51
|
const fn =
|
|
50
|
-
|
|
52
|
+
decoratedNode.body.length === 0
|
|
51
53
|
? () => {}
|
|
52
|
-
:
|
|
54
|
+
: decoratedNode.body.length === 1
|
|
53
55
|
? expressions[0]
|
|
54
56
|
: function (scope, locals) {
|
|
55
57
|
let lastValue;
|
|
56
|
-
forEach(
|
|
58
|
+
expressions.forEach((exp) => {
|
|
57
59
|
lastValue = exp(scope, locals);
|
|
58
60
|
});
|
|
59
61
|
return lastValue;
|
|
60
62
|
};
|
|
61
63
|
if (assign) {
|
|
62
|
-
fn.assign =
|
|
63
|
-
return assign(scope, locals, value);
|
|
64
|
-
};
|
|
64
|
+
fn.assign = (scope, value, locals) => assign(scope, locals, value);
|
|
65
65
|
}
|
|
66
66
|
if (inputs) {
|
|
67
67
|
fn.inputs = inputs;
|
|
@@ -73,17 +73,14 @@ export class ASTInterpreter {
|
|
|
73
73
|
* Recurses the AST nodes.
|
|
74
74
|
* @param {import("./ast").ASTNode} ast - The AST node.
|
|
75
75
|
* @param {Object} [context] - The context.
|
|
76
|
-
* @param {boolean|
|
|
77
|
-
* @returns {
|
|
76
|
+
* @param {boolean|1} [create] - The create flag.
|
|
77
|
+
* @returns {import("./parse").CompiledExpressionFunction} The recursive function.
|
|
78
78
|
*/
|
|
79
79
|
recurse(ast, context, create) {
|
|
80
80
|
let left;
|
|
81
81
|
let right;
|
|
82
82
|
const self = this;
|
|
83
83
|
let args;
|
|
84
|
-
if (ast.input) {
|
|
85
|
-
return this.inputs(ast.input, ast.watchId);
|
|
86
|
-
}
|
|
87
84
|
switch (ast.type) {
|
|
88
85
|
case ASTType.Literal:
|
|
89
86
|
return this.value(ast.value, context);
|
|
@@ -114,33 +111,43 @@ export class ASTInterpreter {
|
|
|
114
111
|
}
|
|
115
112
|
if (ast.computed) right = this.recurse(ast.property);
|
|
116
113
|
return ast.computed
|
|
117
|
-
? this.computedMember(
|
|
118
|
-
|
|
114
|
+
? this.computedMember(
|
|
115
|
+
left,
|
|
116
|
+
/** @type {function } */ (right),
|
|
117
|
+
context,
|
|
118
|
+
create,
|
|
119
|
+
)
|
|
120
|
+
: this.nonComputedMember(
|
|
121
|
+
left,
|
|
122
|
+
/** @type {string } */ (right),
|
|
123
|
+
context,
|
|
124
|
+
create,
|
|
125
|
+
);
|
|
119
126
|
case ASTType.CallExpression:
|
|
120
127
|
args = [];
|
|
121
|
-
|
|
128
|
+
ast.arguments.forEach((expr) => {
|
|
122
129
|
args.push(self.recurse(expr));
|
|
123
130
|
});
|
|
124
131
|
if (ast.filter) right = this.$filter(ast.callee.name);
|
|
125
132
|
if (!ast.filter) right = this.recurse(ast.callee, true);
|
|
126
133
|
return ast.filter
|
|
127
|
-
? function (scope, locals, assign
|
|
134
|
+
? function (scope, locals, assign) {
|
|
128
135
|
const values = [];
|
|
129
136
|
for (let i = 0; i < args.length; ++i) {
|
|
130
|
-
values.push(args[i](scope, locals, assign
|
|
137
|
+
values.push(args[i](scope, locals, assign));
|
|
131
138
|
}
|
|
132
|
-
const value = right.apply(undefined, values
|
|
139
|
+
const value = right.apply(undefined, values);
|
|
133
140
|
return context
|
|
134
141
|
? { context: undefined, name: undefined, value }
|
|
135
142
|
: value;
|
|
136
143
|
}
|
|
137
|
-
: function (scope, locals, assign
|
|
138
|
-
const rhs = right(scope, locals, assign
|
|
144
|
+
: function (scope, locals, assign) {
|
|
145
|
+
const rhs = right(scope, locals, assign);
|
|
139
146
|
let value;
|
|
140
147
|
if (rhs.value != null) {
|
|
141
148
|
const values = [];
|
|
142
149
|
for (let i = 0; i < args.length; ++i) {
|
|
143
|
-
values.push(args[i](scope, locals, assign
|
|
150
|
+
values.push(args[i](scope, locals, assign));
|
|
144
151
|
}
|
|
145
152
|
value = rhs.value.apply(rhs.context, values);
|
|
146
153
|
}
|
|
@@ -149,27 +156,27 @@ export class ASTInterpreter {
|
|
|
149
156
|
case ASTType.AssignmentExpression:
|
|
150
157
|
left = this.recurse(ast.left, true, 1);
|
|
151
158
|
right = this.recurse(ast.right);
|
|
152
|
-
return function (scope, locals, assign
|
|
153
|
-
const lhs = left(scope, locals, assign
|
|
154
|
-
const rhs = right(scope, locals, assign
|
|
159
|
+
return function (scope, locals, assign) {
|
|
160
|
+
const lhs = left(scope, locals, assign);
|
|
161
|
+
const rhs = right(scope, locals, assign);
|
|
155
162
|
lhs.context[lhs.name] = rhs;
|
|
156
163
|
return context ? { value: rhs } : rhs;
|
|
157
164
|
};
|
|
158
165
|
case ASTType.ArrayExpression:
|
|
159
166
|
args = [];
|
|
160
|
-
|
|
167
|
+
ast.elements.forEach((expr) => {
|
|
161
168
|
args.push(self.recurse(expr));
|
|
162
169
|
});
|
|
163
|
-
return function (scope, locals, assign
|
|
170
|
+
return function (scope, locals, assign) {
|
|
164
171
|
const value = [];
|
|
165
172
|
for (let i = 0; i < args.length; ++i) {
|
|
166
|
-
value.push(args[i](scope, locals, assign
|
|
173
|
+
value.push(args[i](scope, locals, assign));
|
|
167
174
|
}
|
|
168
175
|
return context ? { value } : value;
|
|
169
176
|
};
|
|
170
177
|
case ASTType.ObjectExpression:
|
|
171
178
|
args = [];
|
|
172
|
-
|
|
179
|
+
ast.properties.forEach((property) => {
|
|
173
180
|
if (property.computed) {
|
|
174
181
|
args.push({
|
|
175
182
|
key: self.recurse(property.key),
|
|
@@ -187,18 +194,17 @@ export class ASTInterpreter {
|
|
|
187
194
|
});
|
|
188
195
|
}
|
|
189
196
|
});
|
|
190
|
-
return function (scope, locals, assign
|
|
197
|
+
return function (scope, locals, assign) {
|
|
191
198
|
const value = {};
|
|
192
199
|
for (let i = 0; i < args.length; ++i) {
|
|
193
200
|
if (args[i].computed) {
|
|
194
|
-
value[args[i].key(scope, locals, assign
|
|
201
|
+
value[args[i].key(scope, locals, assign)] = args[i].value(
|
|
195
202
|
scope,
|
|
196
203
|
locals,
|
|
197
204
|
assign,
|
|
198
|
-
inputs,
|
|
199
205
|
);
|
|
200
206
|
} else {
|
|
201
|
-
value[args[i].key] = args[i].value(scope, locals, assign
|
|
207
|
+
value[args[i].key] = args[i].value(scope, locals, assign);
|
|
202
208
|
}
|
|
203
209
|
}
|
|
204
210
|
return context ? { value } : value;
|
|
@@ -225,8 +231,8 @@ export class ASTInterpreter {
|
|
|
225
231
|
* @returns {function} The unary plus function.
|
|
226
232
|
*/
|
|
227
233
|
"unary+"(argument, context) {
|
|
228
|
-
return function (scope, locals, assign
|
|
229
|
-
let arg = argument(scope, locals, assign
|
|
234
|
+
return function (scope, locals, assign) {
|
|
235
|
+
let arg = argument(scope, locals, assign);
|
|
230
236
|
if (isDefined(arg)) {
|
|
231
237
|
arg = +arg;
|
|
232
238
|
} else {
|
|
@@ -243,8 +249,8 @@ export class ASTInterpreter {
|
|
|
243
249
|
* @returns {function} The unary minus function.
|
|
244
250
|
*/
|
|
245
251
|
"unary-"(argument, context) {
|
|
246
|
-
return function (scope, locals, assign
|
|
247
|
-
let arg = argument(scope, locals, assign
|
|
252
|
+
return function (scope, locals, assign) {
|
|
253
|
+
let arg = argument(scope, locals, assign);
|
|
248
254
|
if (isDefined(arg)) {
|
|
249
255
|
arg = -arg;
|
|
250
256
|
} else {
|
|
@@ -261,8 +267,8 @@ export class ASTInterpreter {
|
|
|
261
267
|
* @returns {function} The unary negation function.
|
|
262
268
|
*/
|
|
263
269
|
"unary!"(argument, context) {
|
|
264
|
-
return function (scope, locals, assign
|
|
265
|
-
const arg = !argument(scope, locals, assign
|
|
270
|
+
return function (scope, locals, assign) {
|
|
271
|
+
const arg = !argument(scope, locals, assign);
|
|
266
272
|
return context ? { value: arg } : arg;
|
|
267
273
|
};
|
|
268
274
|
}
|
|
@@ -275,9 +281,9 @@ export class ASTInterpreter {
|
|
|
275
281
|
* @returns {function} The binary plus function.
|
|
276
282
|
*/
|
|
277
283
|
"binary+"(left, right, context) {
|
|
278
|
-
return function (scope, locals, assign
|
|
279
|
-
const lhs = left(scope, locals, assign
|
|
280
|
-
const rhs = right(scope, locals, assign
|
|
284
|
+
return function (scope, locals, assign) {
|
|
285
|
+
const lhs = left(scope, locals, assign);
|
|
286
|
+
const rhs = right(scope, locals, assign);
|
|
281
287
|
const arg = plusFn(lhs, rhs);
|
|
282
288
|
return context ? { value: arg } : arg;
|
|
283
289
|
};
|
|
@@ -291,9 +297,9 @@ export class ASTInterpreter {
|
|
|
291
297
|
* @returns {function} The binary minus function.
|
|
292
298
|
*/
|
|
293
299
|
"binary-"(left, right, context) {
|
|
294
|
-
return function (scope, locals, assign
|
|
295
|
-
const lhs = left(scope, locals, assign
|
|
296
|
-
const rhs = right(scope, locals, assign
|
|
300
|
+
return function (scope, locals, assign) {
|
|
301
|
+
const lhs = left(scope, locals, assign);
|
|
302
|
+
const rhs = right(scope, locals, assign);
|
|
297
303
|
const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
|
|
298
304
|
return context ? { value: arg } : arg;
|
|
299
305
|
};
|
|
@@ -307,19 +313,15 @@ export class ASTInterpreter {
|
|
|
307
313
|
* @returns {function} The binary multiplication function.
|
|
308
314
|
*/
|
|
309
315
|
"binary*"(left, right, context) {
|
|
310
|
-
return function (scope, locals, assign
|
|
311
|
-
const arg =
|
|
312
|
-
left(scope, locals, assign, inputs) *
|
|
313
|
-
right(scope, locals, assign, inputs);
|
|
316
|
+
return function (scope, locals, assign) {
|
|
317
|
+
const arg = left(scope, locals, assign) * right(scope, locals, assign);
|
|
314
318
|
return context ? { value: arg } : arg;
|
|
315
319
|
};
|
|
316
320
|
}
|
|
317
321
|
|
|
318
322
|
"binary/"(left, right, context) {
|
|
319
|
-
return function (scope, locals, assign
|
|
320
|
-
const arg =
|
|
321
|
-
left(scope, locals, assign, inputs) /
|
|
322
|
-
right(scope, locals, assign, inputs);
|
|
323
|
+
return function (scope, locals, assign) {
|
|
324
|
+
const arg = left(scope, locals, assign) / right(scope, locals, assign);
|
|
323
325
|
return context ? { value: arg } : arg;
|
|
324
326
|
};
|
|
325
327
|
}
|
|
@@ -332,10 +334,8 @@ export class ASTInterpreter {
|
|
|
332
334
|
* @returns {function} The binary division function.
|
|
333
335
|
*/
|
|
334
336
|
"binary%"(left, right, context) {
|
|
335
|
-
return function (scope, locals, assign
|
|
336
|
-
const arg =
|
|
337
|
-
left(scope, locals, assign, inputs) %
|
|
338
|
-
right(scope, locals, assign, inputs);
|
|
337
|
+
return function (scope, locals, assign) {
|
|
338
|
+
const arg = left(scope, locals, assign) % right(scope, locals, assign);
|
|
339
339
|
return context ? { value: arg } : arg;
|
|
340
340
|
};
|
|
341
341
|
}
|
|
@@ -348,10 +348,8 @@ export class ASTInterpreter {
|
|
|
348
348
|
* @returns {function} The binary strict equality function.
|
|
349
349
|
*/
|
|
350
350
|
"binary==="(left, right, context) {
|
|
351
|
-
return function (scope, locals, assign
|
|
352
|
-
const arg =
|
|
353
|
-
left(scope, locals, assign, inputs) ===
|
|
354
|
-
right(scope, locals, assign, inputs);
|
|
351
|
+
return function (scope, locals, assign) {
|
|
352
|
+
const arg = left(scope, locals, assign) === right(scope, locals, assign);
|
|
355
353
|
return context ? { value: arg } : arg;
|
|
356
354
|
};
|
|
357
355
|
}
|
|
@@ -364,10 +362,8 @@ export class ASTInterpreter {
|
|
|
364
362
|
* @returns {function} The binary strict inequality function.
|
|
365
363
|
*/
|
|
366
364
|
"binary!=="(left, right, context) {
|
|
367
|
-
return function (scope, locals, assign
|
|
368
|
-
const arg =
|
|
369
|
-
left(scope, locals, assign, inputs) !==
|
|
370
|
-
right(scope, locals, assign, inputs);
|
|
365
|
+
return function (scope, locals, assign) {
|
|
366
|
+
const arg = left(scope, locals, assign) !== right(scope, locals, assign);
|
|
371
367
|
return context ? { value: arg } : arg;
|
|
372
368
|
};
|
|
373
369
|
}
|
|
@@ -380,10 +376,8 @@ export class ASTInterpreter {
|
|
|
380
376
|
* @returns {function} The binary equality function.
|
|
381
377
|
*/
|
|
382
378
|
"binary=="(left, right, context) {
|
|
383
|
-
return function (scope, locals, assign
|
|
384
|
-
const arg =
|
|
385
|
-
left(scope, locals, assign, inputs) ==
|
|
386
|
-
right(scope, locals, assign, inputs);
|
|
379
|
+
return function (scope, locals, assign) {
|
|
380
|
+
const arg = left(scope, locals, assign) == right(scope, locals, assign);
|
|
387
381
|
return context ? { value: arg } : arg;
|
|
388
382
|
};
|
|
389
383
|
}
|
|
@@ -396,10 +390,8 @@ export class ASTInterpreter {
|
|
|
396
390
|
* @returns {function} The binary inequality function.
|
|
397
391
|
*/
|
|
398
392
|
"binary!="(left, right, context) {
|
|
399
|
-
return function (scope, locals, assign
|
|
400
|
-
const arg =
|
|
401
|
-
left(scope, locals, assign, inputs) !=
|
|
402
|
-
right(scope, locals, assign, inputs);
|
|
393
|
+
return function (scope, locals, assign) {
|
|
394
|
+
const arg = left(scope, locals, assign) != right(scope, locals, assign);
|
|
403
395
|
return context ? { value: arg } : arg;
|
|
404
396
|
};
|
|
405
397
|
}
|
|
@@ -412,10 +404,8 @@ export class ASTInterpreter {
|
|
|
412
404
|
* @returns {function} The binary less-than function.
|
|
413
405
|
*/
|
|
414
406
|
"binary<"(left, right, context) {
|
|
415
|
-
return function (scope, locals, assign
|
|
416
|
-
const arg =
|
|
417
|
-
left(scope, locals, assign, inputs) <
|
|
418
|
-
right(scope, locals, assign, inputs);
|
|
407
|
+
return function (scope, locals, assign) {
|
|
408
|
+
const arg = left(scope, locals, assign) < right(scope, locals, assign);
|
|
419
409
|
return context ? { value: arg } : arg;
|
|
420
410
|
};
|
|
421
411
|
}
|
|
@@ -428,10 +418,8 @@ export class ASTInterpreter {
|
|
|
428
418
|
* @returns {function} The binary greater-than function.
|
|
429
419
|
*/
|
|
430
420
|
"binary>"(left, right, context) {
|
|
431
|
-
return function (scope, locals, assign
|
|
432
|
-
const arg =
|
|
433
|
-
left(scope, locals, assign, inputs) >
|
|
434
|
-
right(scope, locals, assign, inputs);
|
|
421
|
+
return function (scope, locals, assign) {
|
|
422
|
+
const arg = left(scope, locals, assign) > right(scope, locals, assign);
|
|
435
423
|
return context ? { value: arg } : arg;
|
|
436
424
|
};
|
|
437
425
|
}
|
|
@@ -444,10 +432,8 @@ export class ASTInterpreter {
|
|
|
444
432
|
* @returns {function} The binary less-than-or-equal-to function.
|
|
445
433
|
*/
|
|
446
434
|
"binary<="(left, right, context) {
|
|
447
|
-
return function (scope, locals, assign
|
|
448
|
-
const arg =
|
|
449
|
-
left(scope, locals, assign, inputs) <=
|
|
450
|
-
right(scope, locals, assign, inputs);
|
|
435
|
+
return function (scope, locals, assign) {
|
|
436
|
+
const arg = left(scope, locals, assign) <= right(scope, locals, assign);
|
|
451
437
|
return context ? { value: arg } : arg;
|
|
452
438
|
};
|
|
453
439
|
}
|
|
@@ -460,10 +446,8 @@ export class ASTInterpreter {
|
|
|
460
446
|
* @returns {function} The binary greater-than-or-equal-to function.
|
|
461
447
|
*/
|
|
462
448
|
"binary>="(left, right, context) {
|
|
463
|
-
return function (scope, locals, assign
|
|
464
|
-
const arg =
|
|
465
|
-
left(scope, locals, assign, inputs) >=
|
|
466
|
-
right(scope, locals, assign, inputs);
|
|
449
|
+
return function (scope, locals, assign) {
|
|
450
|
+
const arg = left(scope, locals, assign) >= right(scope, locals, assign);
|
|
467
451
|
return context ? { value: arg } : arg;
|
|
468
452
|
};
|
|
469
453
|
}
|
|
@@ -475,10 +459,8 @@ export class ASTInterpreter {
|
|
|
475
459
|
* @returns {function} The binary logical AND function.
|
|
476
460
|
*/
|
|
477
461
|
"binary&&"(left, right, context) {
|
|
478
|
-
return function (scope, locals, assign
|
|
479
|
-
const arg =
|
|
480
|
-
left(scope, locals, assign, inputs) &&
|
|
481
|
-
right(scope, locals, assign, inputs);
|
|
462
|
+
return function (scope, locals, assign) {
|
|
463
|
+
const arg = left(scope, locals, assign) && right(scope, locals, assign);
|
|
482
464
|
return context ? { value: arg } : arg;
|
|
483
465
|
};
|
|
484
466
|
}
|
|
@@ -491,10 +473,8 @@ export class ASTInterpreter {
|
|
|
491
473
|
* @returns {function} The binary logical OR function.
|
|
492
474
|
*/
|
|
493
475
|
"binary||"(left, right, context) {
|
|
494
|
-
return function (scope, locals, assign
|
|
495
|
-
const arg =
|
|
496
|
-
left(scope, locals, assign, inputs) ||
|
|
497
|
-
right(scope, locals, assign, inputs);
|
|
476
|
+
return function (scope, locals, assign) {
|
|
477
|
+
const arg = left(scope, locals, assign) || right(scope, locals, assign);
|
|
498
478
|
return context ? { value: arg } : arg;
|
|
499
479
|
};
|
|
500
480
|
}
|
|
@@ -508,10 +488,10 @@ export class ASTInterpreter {
|
|
|
508
488
|
* @returns {function} The ternary conditional function.
|
|
509
489
|
*/
|
|
510
490
|
"ternary?:"(test, alternate, consequent, context) {
|
|
511
|
-
return function (scope, locals, assign
|
|
512
|
-
const arg = test(scope, locals, assign
|
|
513
|
-
? alternate(scope, locals, assign
|
|
514
|
-
: consequent(scope, locals, assign
|
|
491
|
+
return function (scope, locals, assign) {
|
|
492
|
+
const arg = test(scope, locals, assign)
|
|
493
|
+
? alternate(scope, locals, assign)
|
|
494
|
+
: consequent(scope, locals, assign);
|
|
515
495
|
return context ? { value: arg } : arg;
|
|
516
496
|
};
|
|
517
497
|
}
|
|
@@ -532,7 +512,7 @@ export class ASTInterpreter {
|
|
|
532
512
|
* Returns the value of an identifier.
|
|
533
513
|
* @param {string} name - The identifier name.
|
|
534
514
|
* @param {Object} [context] - The context.
|
|
535
|
-
* @param {boolean} [create] - Whether to create the identifier if it does not exist.
|
|
515
|
+
* @param {boolean|1} [create] - Whether to create the identifier if it does not exist.
|
|
536
516
|
* @returns {function} The function returning the identifier value.
|
|
537
517
|
*/
|
|
538
518
|
identifier(name, context, create) {
|
|
@@ -554,16 +534,16 @@ export class ASTInterpreter {
|
|
|
554
534
|
* @param {function} left - The left operand function.
|
|
555
535
|
* @param {function} right - The right operand function.
|
|
556
536
|
* @param {Object} [context] - The context.
|
|
557
|
-
* @param {boolean} [create] - Whether to create the member if it does not exist.
|
|
537
|
+
* @param {boolean|1} [create] - Whether to create the member if it does not exist.
|
|
558
538
|
* @returns {function} The function returning the computed member value.
|
|
559
539
|
*/
|
|
560
540
|
computedMember(left, right, context, create) {
|
|
561
|
-
return function (scope, locals, assign
|
|
562
|
-
const lhs = left(scope, locals, assign
|
|
541
|
+
return function (scope, locals, assign) {
|
|
542
|
+
const lhs = left(scope, locals, assign);
|
|
563
543
|
let rhs;
|
|
564
544
|
let value;
|
|
565
545
|
if (lhs != null) {
|
|
566
|
-
rhs = right(scope, locals, assign
|
|
546
|
+
rhs = right(scope, locals, assign);
|
|
567
547
|
rhs = getStringValue(rhs);
|
|
568
548
|
if (create && create !== 1) {
|
|
569
549
|
if (lhs && !lhs[rhs]) {
|
|
@@ -584,12 +564,12 @@ export class ASTInterpreter {
|
|
|
584
564
|
* @param {function} left - The left operand function.
|
|
585
565
|
* @param {string} right - The right operand function.
|
|
586
566
|
* @param {Object} [context] - The context.
|
|
587
|
-
* @param {boolean} [create] - Whether to create the member if it does not exist.
|
|
567
|
+
* @param {boolean|1} [create] - Whether to create the member if it does not exist.
|
|
588
568
|
* @returns {function} The function returning the non-computed member value.
|
|
589
569
|
*/
|
|
590
570
|
nonComputedMember(left, right, context, create) {
|
|
591
|
-
return function (scope, locals, assign
|
|
592
|
-
const lhs = left(scope, locals, assign
|
|
571
|
+
return function (scope, locals, assign) {
|
|
572
|
+
const lhs = left(scope, locals, assign);
|
|
593
573
|
if (create && create !== 1) {
|
|
594
574
|
if (lhs && lhs[right] == null) {
|
|
595
575
|
lhs[right] = {};
|
|
@@ -602,17 +582,306 @@ export class ASTInterpreter {
|
|
|
602
582
|
return value;
|
|
603
583
|
};
|
|
604
584
|
}
|
|
585
|
+
}
|
|
605
586
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
587
|
+
/**
|
|
588
|
+
* @typedef {import("./ast").ASTNode & {
|
|
589
|
+
* isPure: boolean|number,
|
|
590
|
+
* constant: boolean,
|
|
591
|
+
* toWatch: Array,
|
|
592
|
+
* }} DecoratedASTNode
|
|
593
|
+
*/
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Decorates AST with constant, toWatch, and isPure properties
|
|
597
|
+
* @param {import("./ast").ASTNode} ast
|
|
598
|
+
* @param {function(any):any} $filter
|
|
599
|
+
* @param {boolean|1|2} [parentIsPure]
|
|
600
|
+
* @returns {DecoratedASTNode}
|
|
601
|
+
*/
|
|
602
|
+
function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
|
603
|
+
let allConstants;
|
|
604
|
+
let argsToWatch;
|
|
605
|
+
let isStatelessFilter;
|
|
606
|
+
let decoratedNode = /** @type {DecoratedASTNode} */ (ast);
|
|
607
|
+
let decoratedLeft,
|
|
608
|
+
decoratedRight,
|
|
609
|
+
decoratedTest,
|
|
610
|
+
decoratedAlternate,
|
|
611
|
+
decoratedConsequent,
|
|
612
|
+
decoratedObject,
|
|
613
|
+
decoratedProperty,
|
|
614
|
+
decoratedKey;
|
|
615
|
+
const astIsPure = (decoratedNode.isPure = isPure(ast, parentIsPure));
|
|
616
|
+
|
|
617
|
+
switch (ast.type) {
|
|
618
|
+
case ASTType.Program:
|
|
619
|
+
allConstants = true;
|
|
620
|
+
decoratedNode.body.forEach((expr) => {
|
|
621
|
+
let decorated = findConstantAndWatchExpressions(
|
|
622
|
+
expr.expression,
|
|
623
|
+
$filter,
|
|
624
|
+
astIsPure,
|
|
625
|
+
);
|
|
626
|
+
allConstants = allConstants && decorated.constant;
|
|
627
|
+
});
|
|
628
|
+
decoratedNode.constant = allConstants;
|
|
629
|
+
return decoratedNode;
|
|
630
|
+
case ASTType.Literal:
|
|
631
|
+
decoratedNode.constant = true;
|
|
632
|
+
decoratedNode.toWatch = [];
|
|
633
|
+
return decoratedNode;
|
|
634
|
+
case ASTType.UnaryExpression:
|
|
635
|
+
var decorated = findConstantAndWatchExpressions(
|
|
636
|
+
decoratedNode.argument,
|
|
637
|
+
$filter,
|
|
638
|
+
astIsPure,
|
|
639
|
+
);
|
|
640
|
+
decoratedNode.constant = decorated.constant;
|
|
641
|
+
decoratedNode.toWatch = decorated.toWatch;
|
|
642
|
+
return decoratedNode;
|
|
643
|
+
case ASTType.BinaryExpression:
|
|
644
|
+
decoratedLeft = findConstantAndWatchExpressions(
|
|
645
|
+
decoratedNode.left,
|
|
646
|
+
$filter,
|
|
647
|
+
astIsPure,
|
|
648
|
+
);
|
|
649
|
+
decoratedRight = findConstantAndWatchExpressions(
|
|
650
|
+
decoratedNode.right,
|
|
651
|
+
$filter,
|
|
652
|
+
astIsPure,
|
|
653
|
+
);
|
|
654
|
+
decoratedNode.constant =
|
|
655
|
+
decoratedLeft.constant && decoratedRight.constant;
|
|
656
|
+
decoratedNode.toWatch = decoratedLeft.toWatch.concat(
|
|
657
|
+
decoratedRight.toWatch,
|
|
658
|
+
);
|
|
659
|
+
return decoratedNode;
|
|
660
|
+
case ASTType.LogicalExpression:
|
|
661
|
+
decoratedLeft = findConstantAndWatchExpressions(
|
|
662
|
+
decoratedNode.left,
|
|
663
|
+
$filter,
|
|
664
|
+
astIsPure,
|
|
665
|
+
);
|
|
666
|
+
decoratedRight = findConstantAndWatchExpressions(
|
|
667
|
+
decoratedNode.right,
|
|
668
|
+
$filter,
|
|
669
|
+
astIsPure,
|
|
670
|
+
);
|
|
671
|
+
decoratedNode.constant =
|
|
672
|
+
decoratedLeft.constant && decoratedRight.constant;
|
|
673
|
+
decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
|
|
674
|
+
return decoratedNode;
|
|
675
|
+
case ASTType.ConditionalExpression:
|
|
676
|
+
decoratedTest = findConstantAndWatchExpressions(
|
|
677
|
+
ast.test,
|
|
678
|
+
$filter,
|
|
679
|
+
astIsPure,
|
|
680
|
+
);
|
|
681
|
+
decoratedAlternate = findConstantAndWatchExpressions(
|
|
682
|
+
ast.alternate,
|
|
683
|
+
$filter,
|
|
684
|
+
astIsPure,
|
|
685
|
+
);
|
|
686
|
+
decoratedConsequent = findConstantAndWatchExpressions(
|
|
687
|
+
ast.consequent,
|
|
688
|
+
$filter,
|
|
689
|
+
astIsPure,
|
|
690
|
+
);
|
|
691
|
+
decoratedNode.constant =
|
|
692
|
+
decoratedTest.constant &&
|
|
693
|
+
decoratedAlternate.constant &&
|
|
694
|
+
decoratedConsequent.constant;
|
|
695
|
+
decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
|
|
696
|
+
return decoratedNode;
|
|
697
|
+
case ASTType.Identifier:
|
|
698
|
+
decoratedNode.constant = false;
|
|
699
|
+
decoratedNode.toWatch = [ast];
|
|
700
|
+
return decoratedNode;
|
|
701
|
+
case ASTType.MemberExpression:
|
|
702
|
+
decoratedObject = findConstantAndWatchExpressions(
|
|
703
|
+
ast.object,
|
|
704
|
+
$filter,
|
|
705
|
+
astIsPure,
|
|
706
|
+
);
|
|
707
|
+
if (ast.computed) {
|
|
708
|
+
decoratedProperty = findConstantAndWatchExpressions(
|
|
709
|
+
ast.property,
|
|
710
|
+
$filter,
|
|
711
|
+
astIsPure,
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
decoratedNode.constant =
|
|
715
|
+
decoratedObject.constant &&
|
|
716
|
+
(!decoratedNode.computed || decoratedProperty.constant);
|
|
717
|
+
decoratedNode.toWatch = decoratedNode.constant ? [] : [ast];
|
|
718
|
+
return decoratedNode;
|
|
719
|
+
case ASTType.CallExpression:
|
|
720
|
+
isStatelessFilter = ast.filter
|
|
721
|
+
? isStateless($filter, ast.callee.name)
|
|
722
|
+
: false;
|
|
723
|
+
allConstants = isStatelessFilter;
|
|
724
|
+
argsToWatch = [];
|
|
725
|
+
ast.arguments.forEach((expr) => {
|
|
726
|
+
decorated = findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
|
727
|
+
allConstants = allConstants && decorated.constant;
|
|
728
|
+
argsToWatch.push.apply(argsToWatch, decorated.toWatch);
|
|
729
|
+
});
|
|
730
|
+
decoratedNode.constant = allConstants;
|
|
731
|
+
decoratedNode.toWatch = isStatelessFilter ? argsToWatch : [decoratedNode];
|
|
732
|
+
return decoratedNode;
|
|
733
|
+
case ASTType.AssignmentExpression:
|
|
734
|
+
decoratedLeft = findConstantAndWatchExpressions(
|
|
735
|
+
ast.left,
|
|
736
|
+
$filter,
|
|
737
|
+
astIsPure,
|
|
738
|
+
);
|
|
739
|
+
decoratedRight = findConstantAndWatchExpressions(
|
|
740
|
+
ast.right,
|
|
741
|
+
$filter,
|
|
742
|
+
astIsPure,
|
|
743
|
+
);
|
|
744
|
+
decoratedNode.constant =
|
|
745
|
+
decoratedLeft.constant && decoratedRight.constant;
|
|
746
|
+
decoratedNode.toWatch = [decoratedNode];
|
|
747
|
+
return decoratedNode;
|
|
748
|
+
case ASTType.ArrayExpression:
|
|
749
|
+
allConstants = true;
|
|
750
|
+
argsToWatch = [];
|
|
751
|
+
ast.elements.forEach((expr) => {
|
|
752
|
+
decorated = findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
|
753
|
+
allConstants = allConstants && decorated.constant;
|
|
754
|
+
argsToWatch.push.apply(argsToWatch, decorated.toWatch);
|
|
755
|
+
});
|
|
756
|
+
decoratedNode.constant = allConstants;
|
|
757
|
+
decoratedNode.toWatch = argsToWatch;
|
|
758
|
+
return decoratedNode;
|
|
759
|
+
case ASTType.ObjectExpression:
|
|
760
|
+
allConstants = true;
|
|
761
|
+
argsToWatch = [];
|
|
762
|
+
ast.properties.forEach((property) => {
|
|
763
|
+
decorated = findConstantAndWatchExpressions(
|
|
764
|
+
property.value,
|
|
765
|
+
$filter,
|
|
766
|
+
astIsPure,
|
|
767
|
+
);
|
|
768
|
+
allConstants = allConstants && decorated.constant;
|
|
769
|
+
argsToWatch.push.apply(argsToWatch, decorated.toWatch);
|
|
770
|
+
if (property.computed) {
|
|
771
|
+
// `{[key]: value}` implicitly does `key.toString()` which may be non-pure
|
|
772
|
+
decoratedKey = findConstantAndWatchExpressions(
|
|
773
|
+
property.key,
|
|
774
|
+
$filter,
|
|
775
|
+
false,
|
|
776
|
+
);
|
|
777
|
+
allConstants = allConstants && decoratedKey.constant;
|
|
778
|
+
argsToWatch.push.apply(argsToWatch, decoratedKey.toWatch);
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
decoratedNode.constant = allConstants;
|
|
782
|
+
decoratedNode.toWatch = argsToWatch;
|
|
783
|
+
return decoratedNode;
|
|
784
|
+
case ASTType.ThisExpression:
|
|
785
|
+
decoratedNode.constant = false;
|
|
786
|
+
decoratedNode.toWatch = [];
|
|
787
|
+
return decoratedNode;
|
|
788
|
+
case ASTType.LocalsExpression:
|
|
789
|
+
decoratedNode.constant = false;
|
|
790
|
+
decoratedNode.toWatch = [];
|
|
791
|
+
return decoratedNode;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Converts a single expression AST node into an assignment expression if the expression is assignable.
|
|
797
|
+
*
|
|
798
|
+
* @param {import("./ast").ASTNode} ast
|
|
799
|
+
* @returns {import("./ast").ASTNode}
|
|
800
|
+
*/
|
|
801
|
+
function assignableAST(ast) {
|
|
802
|
+
if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
|
|
803
|
+
return {
|
|
804
|
+
type: ASTType.AssignmentExpression,
|
|
805
|
+
left: ast.body[0].expression,
|
|
806
|
+
right: { type: ASTType.NGValueParameter },
|
|
807
|
+
operator: "=",
|
|
616
808
|
};
|
|
617
809
|
}
|
|
618
810
|
}
|
|
811
|
+
|
|
812
|
+
function plusFn(l, r) {
|
|
813
|
+
if (typeof l === "undefined") return r;
|
|
814
|
+
if (typeof r === "undefined") return l;
|
|
815
|
+
return l + r;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
*
|
|
820
|
+
* @param {import("./ast").ASTNode[]} body
|
|
821
|
+
* @returns {any}
|
|
822
|
+
*/
|
|
823
|
+
function getInputs(body) {
|
|
824
|
+
if (body.length !== 1) return;
|
|
825
|
+
const lastExpression = /** @type {DecoratedASTNode} */ (body[0].expression);
|
|
826
|
+
const candidate = lastExpression.toWatch;
|
|
827
|
+
if (candidate.length !== 1) return candidate;
|
|
828
|
+
return candidate[0] !== lastExpression ? candidate : undefined;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Detect nodes which could depend on non-shallow state of objects
|
|
833
|
+
* @param {import("./ast").ASTNode} node
|
|
834
|
+
* @param {boolean|PURITY_ABSOLUTE|PURITY_RELATIVE} parentIsPure
|
|
835
|
+
* @returns {boolean|PURITY_ABSOLUTE|PURITY_RELATIVE}
|
|
836
|
+
*/
|
|
837
|
+
function isPure(node, parentIsPure) {
|
|
838
|
+
switch (node.type) {
|
|
839
|
+
// Computed members might invoke a stateful toString()
|
|
840
|
+
case ASTType.MemberExpression:
|
|
841
|
+
if (node.computed) {
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
break;
|
|
845
|
+
|
|
846
|
+
// Unary always convert to primitive
|
|
847
|
+
case ASTType.UnaryExpression:
|
|
848
|
+
return PURITY_ABSOLUTE;
|
|
849
|
+
|
|
850
|
+
// The binary + operator can invoke a stateful toString().
|
|
851
|
+
case ASTType.BinaryExpression:
|
|
852
|
+
return node.operator !== "+" ? PURITY_ABSOLUTE : false;
|
|
853
|
+
|
|
854
|
+
// Functions / filters probably read state from within objects
|
|
855
|
+
case ASTType.CallExpression:
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function isStateless($filter, filterName) {
|
|
863
|
+
const fn = $filter(filterName);
|
|
864
|
+
return !fn.$stateful;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Converts parameter to strings property name for use as keys in an object.
|
|
869
|
+
* Any non-string object, including a number, is typecasted into a string via the toString method.
|
|
870
|
+
* {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names}
|
|
871
|
+
*
|
|
872
|
+
* @param {!any} name
|
|
873
|
+
* @returns {string}
|
|
874
|
+
*/
|
|
875
|
+
function getStringValue(name) {
|
|
876
|
+
return `${name}`;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* @param {import("./ast").ASTNode} ast
|
|
881
|
+
* @returns {boolean}
|
|
882
|
+
*/
|
|
883
|
+
export function isAssignable(ast) {
|
|
884
|
+
return (
|
|
885
|
+
ast.type === ASTType.Identifier || ast.type === ASTType.MemberExpression
|
|
886
|
+
);
|
|
887
|
+
}
|