@angular-wave/angular.ts 0.0.49 → 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/interpolate/interpolate.js +1 -18
- package/src/core/parser/ast-type.js +21 -20
- package/src/core/parser/ast.js +253 -93
- package/src/core/parser/interpreter.js +639 -174
- package/src/core/parser/lexer.js +22 -22
- package/src/core/parser/parse.js +51 -265
- package/src/core/parser/parse.md +0 -13
- package/src/core/parser/parse.spec.js +429 -444
- package/src/core/parser/parser.js +39 -11
- package/src/directive/csp.md +0 -26
- package/src/loader.js +5 -1
- package/src/shared/jqlite/jqlite.js +2 -2
- package/src/types.js +0 -10
- 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 +266 -73
- package/types/core/parser/interpreter.d.ts +207 -53
- package/types/core/parser/lexer.d.ts +23 -19
- package/types/core/parser/parse.d.ts +50 -44
- package/types/core/parser/parser.d.ts +31 -16
- package/types/loader.d.ts +397 -0
- package/types/shared/jqlite/jqlite.d.ts +4 -4
- package/types/types.d.ts +0 -1
- package/src/core/parser/compiler.js +0 -561
- package/src/core/parser/shared.js +0 -228
- package/types/core/parser/compiler.d.ts +0 -49
- package/types/core/parser/shared.d.ts +0 -29
|
@@ -1,73 +1,86 @@
|
|
|
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
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
export const PURITY_ABSOLUTE = 1;
|
|
5
|
+
export const PURITY_RELATIVE = 2;
|
|
6
|
+
|
|
7
|
+
export class ASTInterpreter {
|
|
8
|
+
/**
|
|
9
|
+
* @param {function(any):any} $filter
|
|
10
|
+
*/
|
|
11
|
+
constructor($filter) {
|
|
12
|
+
this.$filter = $filter;
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Compiles the AST into a function.
|
|
17
|
+
* @param {import("./ast").ASTNode} ast - The AST to compile.
|
|
18
|
+
* @returns {import("./parse").CompiledExpression}
|
|
19
|
+
*/
|
|
16
20
|
compile(ast) {
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
let decoratedNode = findConstantAndWatchExpressions(ast, this.$filter);
|
|
22
|
+
/** @type {import("./ast").ASTNode} */
|
|
19
23
|
let assignable;
|
|
24
|
+
/** @type {import("./parse").CompiledExpression} */
|
|
20
25
|
let assign;
|
|
21
|
-
if ((assignable = assignableAST(
|
|
22
|
-
assign =
|
|
26
|
+
if ((assignable = assignableAST(decoratedNode))) {
|
|
27
|
+
assign = /** @type {import("./parse").CompiledExpression} */ (
|
|
28
|
+
this.recurse(assignable)
|
|
29
|
+
);
|
|
23
30
|
}
|
|
24
|
-
const toWatch = getInputs(
|
|
31
|
+
const toWatch = getInputs(decoratedNode.body);
|
|
25
32
|
let inputs;
|
|
26
33
|
if (toWatch) {
|
|
27
34
|
inputs = [];
|
|
28
|
-
|
|
29
|
-
const input =
|
|
35
|
+
for (const [key, watch] of Object.entries(toWatch)) {
|
|
36
|
+
const input = /** @type {import("./parse").CompiledExpression} */ (
|
|
37
|
+
this.recurse(watch)
|
|
38
|
+
);
|
|
30
39
|
input.isPure = watch.isPure;
|
|
31
40
|
watch.input = input;
|
|
32
41
|
inputs.push(input);
|
|
33
42
|
watch.watchId = key;
|
|
34
|
-
}
|
|
43
|
+
}
|
|
35
44
|
}
|
|
36
45
|
const expressions = [];
|
|
37
|
-
|
|
38
|
-
expressions.push(
|
|
46
|
+
decoratedNode.body.forEach((expression) => {
|
|
47
|
+
expressions.push(this.recurse(expression.expression));
|
|
39
48
|
});
|
|
49
|
+
|
|
50
|
+
/** @type {import("./parse").CompiledExpression} */
|
|
40
51
|
const fn =
|
|
41
|
-
|
|
52
|
+
decoratedNode.body.length === 0
|
|
42
53
|
? () => {}
|
|
43
|
-
:
|
|
54
|
+
: decoratedNode.body.length === 1
|
|
44
55
|
? expressions[0]
|
|
45
56
|
: function (scope, locals) {
|
|
46
57
|
let lastValue;
|
|
47
|
-
forEach(
|
|
58
|
+
expressions.forEach((exp) => {
|
|
48
59
|
lastValue = exp(scope, locals);
|
|
49
60
|
});
|
|
50
61
|
return lastValue;
|
|
51
62
|
};
|
|
52
63
|
if (assign) {
|
|
53
|
-
fn.assign =
|
|
54
|
-
return assign(scope, locals, value);
|
|
55
|
-
};
|
|
64
|
+
fn.assign = (scope, value, locals) => assign(scope, locals, value);
|
|
56
65
|
}
|
|
57
66
|
if (inputs) {
|
|
58
67
|
fn.inputs = inputs;
|
|
59
68
|
}
|
|
60
69
|
return fn;
|
|
61
|
-
}
|
|
70
|
+
}
|
|
62
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Recurses the AST nodes.
|
|
74
|
+
* @param {import("./ast").ASTNode} ast - The AST node.
|
|
75
|
+
* @param {Object} [context] - The context.
|
|
76
|
+
* @param {boolean|1} [create] - The create flag.
|
|
77
|
+
* @returns {import("./parse").CompiledExpressionFunction} The recursive function.
|
|
78
|
+
*/
|
|
63
79
|
recurse(ast, context, create) {
|
|
64
80
|
let left;
|
|
65
81
|
let right;
|
|
66
82
|
const self = this;
|
|
67
83
|
let args;
|
|
68
|
-
if (ast.input) {
|
|
69
|
-
return this.inputs(ast.input, ast.watchId);
|
|
70
|
-
}
|
|
71
84
|
switch (ast.type) {
|
|
72
85
|
case ASTType.Literal:
|
|
73
86
|
return this.value(ast.value, context);
|
|
@@ -98,33 +111,43 @@ ASTInterpreter.prototype = {
|
|
|
98
111
|
}
|
|
99
112
|
if (ast.computed) right = this.recurse(ast.property);
|
|
100
113
|
return ast.computed
|
|
101
|
-
? this.computedMember(
|
|
102
|
-
|
|
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
|
+
);
|
|
103
126
|
case ASTType.CallExpression:
|
|
104
127
|
args = [];
|
|
105
|
-
|
|
128
|
+
ast.arguments.forEach((expr) => {
|
|
106
129
|
args.push(self.recurse(expr));
|
|
107
130
|
});
|
|
108
131
|
if (ast.filter) right = this.$filter(ast.callee.name);
|
|
109
132
|
if (!ast.filter) right = this.recurse(ast.callee, true);
|
|
110
133
|
return ast.filter
|
|
111
|
-
? function (scope, locals, assign
|
|
134
|
+
? function (scope, locals, assign) {
|
|
112
135
|
const values = [];
|
|
113
136
|
for (let i = 0; i < args.length; ++i) {
|
|
114
|
-
values.push(args[i](scope, locals, assign
|
|
137
|
+
values.push(args[i](scope, locals, assign));
|
|
115
138
|
}
|
|
116
|
-
const value = right.apply(undefined, values
|
|
139
|
+
const value = right.apply(undefined, values);
|
|
117
140
|
return context
|
|
118
141
|
? { context: undefined, name: undefined, value }
|
|
119
142
|
: value;
|
|
120
143
|
}
|
|
121
|
-
: function (scope, locals, assign
|
|
122
|
-
const rhs = right(scope, locals, assign
|
|
144
|
+
: function (scope, locals, assign) {
|
|
145
|
+
const rhs = right(scope, locals, assign);
|
|
123
146
|
let value;
|
|
124
147
|
if (rhs.value != null) {
|
|
125
148
|
const values = [];
|
|
126
149
|
for (let i = 0; i < args.length; ++i) {
|
|
127
|
-
values.push(args[i](scope, locals, assign
|
|
150
|
+
values.push(args[i](scope, locals, assign));
|
|
128
151
|
}
|
|
129
152
|
value = rhs.value.apply(rhs.context, values);
|
|
130
153
|
}
|
|
@@ -133,27 +156,27 @@ ASTInterpreter.prototype = {
|
|
|
133
156
|
case ASTType.AssignmentExpression:
|
|
134
157
|
left = this.recurse(ast.left, true, 1);
|
|
135
158
|
right = this.recurse(ast.right);
|
|
136
|
-
return function (scope, locals, assign
|
|
137
|
-
const lhs = left(scope, locals, assign
|
|
138
|
-
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);
|
|
139
162
|
lhs.context[lhs.name] = rhs;
|
|
140
163
|
return context ? { value: rhs } : rhs;
|
|
141
164
|
};
|
|
142
165
|
case ASTType.ArrayExpression:
|
|
143
166
|
args = [];
|
|
144
|
-
|
|
167
|
+
ast.elements.forEach((expr) => {
|
|
145
168
|
args.push(self.recurse(expr));
|
|
146
169
|
});
|
|
147
|
-
return function (scope, locals, assign
|
|
170
|
+
return function (scope, locals, assign) {
|
|
148
171
|
const value = [];
|
|
149
172
|
for (let i = 0; i < args.length; ++i) {
|
|
150
|
-
value.push(args[i](scope, locals, assign
|
|
173
|
+
value.push(args[i](scope, locals, assign));
|
|
151
174
|
}
|
|
152
175
|
return context ? { value } : value;
|
|
153
176
|
};
|
|
154
177
|
case ASTType.ObjectExpression:
|
|
155
178
|
args = [];
|
|
156
|
-
|
|
179
|
+
ast.properties.forEach((property) => {
|
|
157
180
|
if (property.computed) {
|
|
158
181
|
args.push({
|
|
159
182
|
key: self.recurse(property.key),
|
|
@@ -171,18 +194,17 @@ ASTInterpreter.prototype = {
|
|
|
171
194
|
});
|
|
172
195
|
}
|
|
173
196
|
});
|
|
174
|
-
return function (scope, locals, assign
|
|
197
|
+
return function (scope, locals, assign) {
|
|
175
198
|
const value = {};
|
|
176
199
|
for (let i = 0; i < args.length; ++i) {
|
|
177
200
|
if (args[i].computed) {
|
|
178
|
-
value[args[i].key(scope, locals, assign
|
|
201
|
+
value[args[i].key(scope, locals, assign)] = args[i].value(
|
|
179
202
|
scope,
|
|
180
203
|
locals,
|
|
181
204
|
assign,
|
|
182
|
-
inputs,
|
|
183
205
|
);
|
|
184
206
|
} else {
|
|
185
|
-
value[args[i].key] = args[i].value(scope, locals, assign
|
|
207
|
+
value[args[i].key] = args[i].value(scope, locals, assign);
|
|
186
208
|
}
|
|
187
209
|
}
|
|
188
210
|
return context ? { value } : value;
|
|
@@ -200,11 +222,17 @@ ASTInterpreter.prototype = {
|
|
|
200
222
|
return context ? { value: assign } : assign;
|
|
201
223
|
};
|
|
202
224
|
}
|
|
203
|
-
}
|
|
225
|
+
}
|
|
204
226
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Unary plus operation.
|
|
229
|
+
* @param {function} argument - The argument function.
|
|
230
|
+
* @param {Object} [context] - The context.
|
|
231
|
+
* @returns {function} The unary plus function.
|
|
232
|
+
*/
|
|
233
|
+
"unary+"(argument, context) {
|
|
234
|
+
return function (scope, locals, assign) {
|
|
235
|
+
let arg = argument(scope, locals, assign);
|
|
208
236
|
if (isDefined(arg)) {
|
|
209
237
|
arg = +arg;
|
|
210
238
|
} else {
|
|
@@ -212,11 +240,17 @@ ASTInterpreter.prototype = {
|
|
|
212
240
|
}
|
|
213
241
|
return context ? { value: arg } : arg;
|
|
214
242
|
};
|
|
215
|
-
}
|
|
243
|
+
}
|
|
216
244
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Unary minus operation.
|
|
247
|
+
* @param {function} argument - The argument function.
|
|
248
|
+
* @param {Object} [context] - The context.
|
|
249
|
+
* @returns {function} The unary minus function.
|
|
250
|
+
*/
|
|
251
|
+
"unary-"(argument, context) {
|
|
252
|
+
return function (scope, locals, assign) {
|
|
253
|
+
let arg = argument(scope, locals, assign);
|
|
220
254
|
if (isDefined(arg)) {
|
|
221
255
|
arg = -arg;
|
|
222
256
|
} else {
|
|
@@ -224,146 +258,263 @@ ASTInterpreter.prototype = {
|
|
|
224
258
|
}
|
|
225
259
|
return context ? { value: arg } : arg;
|
|
226
260
|
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Unary negation operation.
|
|
265
|
+
* @param {function} argument - The argument function.
|
|
266
|
+
* @param {Object} [context] - The context.
|
|
267
|
+
* @returns {function} The unary negation function.
|
|
268
|
+
*/
|
|
269
|
+
"unary!"(argument, context) {
|
|
270
|
+
return function (scope, locals, assign) {
|
|
271
|
+
const arg = !argument(scope, locals, assign);
|
|
231
272
|
return context ? { value: arg } : arg;
|
|
232
273
|
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Binary plus operation.
|
|
278
|
+
* @param {function} left - The left operand function.
|
|
279
|
+
* @param {function} right - The right operand function.
|
|
280
|
+
* @param {Object} [context] - The context.
|
|
281
|
+
* @returns {function} The binary plus function.
|
|
282
|
+
*/
|
|
283
|
+
"binary+"(left, right, context) {
|
|
284
|
+
return function (scope, locals, assign) {
|
|
285
|
+
const lhs = left(scope, locals, assign);
|
|
286
|
+
const rhs = right(scope, locals, assign);
|
|
238
287
|
const arg = plusFn(lhs, rhs);
|
|
239
288
|
return context ? { value: arg } : arg;
|
|
240
289
|
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Binary minus operation.
|
|
294
|
+
* @param {function} left - The left operand function.
|
|
295
|
+
* @param {function} right - The right operand function.
|
|
296
|
+
* @param {Object} [context] - The context.
|
|
297
|
+
* @returns {function} The binary minus function.
|
|
298
|
+
*/
|
|
299
|
+
"binary-"(left, right, context) {
|
|
300
|
+
return function (scope, locals, assign) {
|
|
301
|
+
const lhs = left(scope, locals, assign);
|
|
302
|
+
const rhs = right(scope, locals, assign);
|
|
246
303
|
const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
|
|
247
304
|
return context ? { value: arg } : arg;
|
|
248
305
|
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Binary multiplication operation.
|
|
310
|
+
* @param {function} left - The left operand function.
|
|
311
|
+
* @param {function} right - The right operand function.
|
|
312
|
+
* @param {Object} [context] - The context.
|
|
313
|
+
* @returns {function} The binary multiplication function.
|
|
314
|
+
*/
|
|
315
|
+
"binary*"(left, right, context) {
|
|
316
|
+
return function (scope, locals, assign) {
|
|
317
|
+
const arg = left(scope, locals, assign) * right(scope, locals, assign);
|
|
255
318
|
return context ? { value: arg } : arg;
|
|
256
319
|
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
right(scope, locals, assign, inputs);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
"binary/"(left, right, context) {
|
|
323
|
+
return function (scope, locals, assign) {
|
|
324
|
+
const arg = left(scope, locals, assign) / right(scope, locals, assign);
|
|
263
325
|
return context ? { value: arg } : arg;
|
|
264
326
|
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Binary division operation.
|
|
331
|
+
* @param {function} left - The left operand function.
|
|
332
|
+
* @param {function} right - The right operand function.
|
|
333
|
+
* @param {Object} [context] - The context.
|
|
334
|
+
* @returns {function} The binary division function.
|
|
335
|
+
*/
|
|
336
|
+
"binary%"(left, right, context) {
|
|
337
|
+
return function (scope, locals, assign) {
|
|
338
|
+
const arg = left(scope, locals, assign) % right(scope, locals, assign);
|
|
271
339
|
return context ? { value: arg } : arg;
|
|
272
340
|
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Binary strict equality operation.
|
|
345
|
+
* @param {function} left - The left operand function.
|
|
346
|
+
* @param {function} right - The right operand function.
|
|
347
|
+
* @param {Object} [context] - The context.
|
|
348
|
+
* @returns {function} The binary strict equality function.
|
|
349
|
+
*/
|
|
350
|
+
"binary==="(left, right, context) {
|
|
351
|
+
return function (scope, locals, assign) {
|
|
352
|
+
const arg = left(scope, locals, assign) === right(scope, locals, assign);
|
|
279
353
|
return context ? { value: arg } : arg;
|
|
280
354
|
};
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Binary strict inequality operation.
|
|
359
|
+
* @param {function} left - The left operand function.
|
|
360
|
+
* @param {function} right - The right operand function.
|
|
361
|
+
* @param {Object} [context] - The context.
|
|
362
|
+
* @returns {function} The binary strict inequality function.
|
|
363
|
+
*/
|
|
364
|
+
"binary!=="(left, right, context) {
|
|
365
|
+
return function (scope, locals, assign) {
|
|
366
|
+
const arg = left(scope, locals, assign) !== right(scope, locals, assign);
|
|
287
367
|
return context ? { value: arg } : arg;
|
|
288
368
|
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Binary equality operation.
|
|
373
|
+
* @param {function} left - The left operand function.
|
|
374
|
+
* @param {function} right - The right operand function.
|
|
375
|
+
* @param {Object} [context] - The context.
|
|
376
|
+
* @returns {function} The binary equality function.
|
|
377
|
+
*/
|
|
378
|
+
"binary=="(left, right, context) {
|
|
379
|
+
return function (scope, locals, assign) {
|
|
380
|
+
const arg = left(scope, locals, assign) == right(scope, locals, assign);
|
|
295
381
|
return context ? { value: arg } : arg;
|
|
296
382
|
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Binary inequality operation.
|
|
387
|
+
* @param {function} left - The left operand function.
|
|
388
|
+
* @param {function} right - The right operand function.
|
|
389
|
+
* @param {Object} [context] - The context.
|
|
390
|
+
* @returns {function} The binary inequality function.
|
|
391
|
+
*/
|
|
392
|
+
"binary!="(left, right, context) {
|
|
393
|
+
return function (scope, locals, assign) {
|
|
394
|
+
const arg = left(scope, locals, assign) != right(scope, locals, assign);
|
|
303
395
|
return context ? { value: arg } : arg;
|
|
304
396
|
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Binary less-than operation.
|
|
401
|
+
* @param {function} left - The left operand function.
|
|
402
|
+
* @param {function} right - The right operand function.
|
|
403
|
+
* @param {Object} [context] - The context.
|
|
404
|
+
* @returns {function} The binary less-than function.
|
|
405
|
+
*/
|
|
406
|
+
"binary<"(left, right, context) {
|
|
407
|
+
return function (scope, locals, assign) {
|
|
408
|
+
const arg = left(scope, locals, assign) < right(scope, locals, assign);
|
|
311
409
|
return context ? { value: arg } : arg;
|
|
312
410
|
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Binary greater-than operation.
|
|
415
|
+
* @param {function} left - The left operand function.
|
|
416
|
+
* @param {function} right - The right operand function.
|
|
417
|
+
* @param {Object} [context] - The context.
|
|
418
|
+
* @returns {function} The binary greater-than function.
|
|
419
|
+
*/
|
|
420
|
+
"binary>"(left, right, context) {
|
|
421
|
+
return function (scope, locals, assign) {
|
|
422
|
+
const arg = left(scope, locals, assign) > right(scope, locals, assign);
|
|
319
423
|
return context ? { value: arg } : arg;
|
|
320
424
|
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Binary less-than-or-equal-to operation.
|
|
429
|
+
* @param {function} left - The left operand function.
|
|
430
|
+
* @param {function} right - The right operand function.
|
|
431
|
+
* @param {Object} [context] - The context.
|
|
432
|
+
* @returns {function} The binary less-than-or-equal-to function.
|
|
433
|
+
*/
|
|
434
|
+
"binary<="(left, right, context) {
|
|
435
|
+
return function (scope, locals, assign) {
|
|
436
|
+
const arg = left(scope, locals, assign) <= right(scope, locals, assign);
|
|
327
437
|
return context ? { value: arg } : arg;
|
|
328
438
|
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Binary greater-than-or-equal-to operation.
|
|
443
|
+
* @param {function} left - The left operand function.
|
|
444
|
+
* @param {function} right - The right operand function.
|
|
445
|
+
* @param {Object} [context] - The context.
|
|
446
|
+
* @returns {function} The binary greater-than-or-equal-to function.
|
|
447
|
+
*/
|
|
448
|
+
"binary>="(left, right, context) {
|
|
449
|
+
return function (scope, locals, assign) {
|
|
450
|
+
const arg = left(scope, locals, assign) >= right(scope, locals, assign);
|
|
335
451
|
return context ? { value: arg } : arg;
|
|
336
452
|
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Binary logical AND operation.
|
|
456
|
+
* @param {function} left - The left operand function.
|
|
457
|
+
* @param {function} right - The right operand function.
|
|
458
|
+
* @param {Object} [context] - The context.
|
|
459
|
+
* @returns {function} The binary logical AND function.
|
|
460
|
+
*/
|
|
461
|
+
"binary&&"(left, right, context) {
|
|
462
|
+
return function (scope, locals, assign) {
|
|
463
|
+
const arg = left(scope, locals, assign) && right(scope, locals, assign);
|
|
343
464
|
return context ? { value: arg } : arg;
|
|
344
465
|
};
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Binary logical OR operation.
|
|
470
|
+
* @param {function} left - The left operand function.
|
|
471
|
+
* @param {function} right - The right operand function.
|
|
472
|
+
* @param {Object} [context] - The context.
|
|
473
|
+
* @returns {function} The binary logical OR function.
|
|
474
|
+
*/
|
|
475
|
+
"binary||"(left, right, context) {
|
|
476
|
+
return function (scope, locals, assign) {
|
|
477
|
+
const arg = left(scope, locals, assign) || right(scope, locals, assign);
|
|
351
478
|
return context ? { value: arg } : arg;
|
|
352
479
|
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Ternary conditional operation.
|
|
484
|
+
* @param {function} test - The test function.
|
|
485
|
+
* @param {function} alternate - The alternate function.
|
|
486
|
+
* @param {function} consequent - The consequent function.
|
|
487
|
+
* @param {Object} [context] - The context.
|
|
488
|
+
* @returns {function} The ternary conditional function.
|
|
489
|
+
*/
|
|
490
|
+
"ternary?:"(test, alternate, consequent, context) {
|
|
491
|
+
return function (scope, locals, assign) {
|
|
492
|
+
const arg = test(scope, locals, assign)
|
|
493
|
+
? alternate(scope, locals, assign)
|
|
494
|
+
: consequent(scope, locals, assign);
|
|
359
495
|
return context ? { value: arg } : arg;
|
|
360
496
|
};
|
|
361
|
-
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Returns the value of a literal.
|
|
501
|
+
* @param {*} value - The literal value.
|
|
502
|
+
* @param {Object} [context] - The context.
|
|
503
|
+
* @returns {function} The function returning the literal value.
|
|
504
|
+
*/
|
|
362
505
|
value(value, context) {
|
|
363
506
|
return function () {
|
|
364
507
|
return context ? { context: undefined, name: undefined, value } : value;
|
|
365
508
|
};
|
|
366
|
-
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Returns the value of an identifier.
|
|
513
|
+
* @param {string} name - The identifier name.
|
|
514
|
+
* @param {Object} [context] - The context.
|
|
515
|
+
* @param {boolean|1} [create] - Whether to create the identifier if it does not exist.
|
|
516
|
+
* @returns {function} The function returning the identifier value.
|
|
517
|
+
*/
|
|
367
518
|
identifier(name, context, create) {
|
|
368
519
|
return function (scope, locals) {
|
|
369
520
|
const base = locals && name in locals ? locals : scope;
|
|
@@ -376,14 +527,23 @@ ASTInterpreter.prototype = {
|
|
|
376
527
|
}
|
|
377
528
|
return value;
|
|
378
529
|
};
|
|
379
|
-
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Returns the value of a computed member expression.
|
|
534
|
+
* @param {function} left - The left operand function.
|
|
535
|
+
* @param {function} right - The right operand function.
|
|
536
|
+
* @param {Object} [context] - The context.
|
|
537
|
+
* @param {boolean|1} [create] - Whether to create the member if it does not exist.
|
|
538
|
+
* @returns {function} The function returning the computed member value.
|
|
539
|
+
*/
|
|
380
540
|
computedMember(left, right, context, create) {
|
|
381
|
-
return function (scope, locals, assign
|
|
382
|
-
const lhs = left(scope, locals, assign
|
|
541
|
+
return function (scope, locals, assign) {
|
|
542
|
+
const lhs = left(scope, locals, assign);
|
|
383
543
|
let rhs;
|
|
384
544
|
let value;
|
|
385
545
|
if (lhs != null) {
|
|
386
|
-
rhs = right(scope, locals, assign
|
|
546
|
+
rhs = right(scope, locals, assign);
|
|
387
547
|
rhs = getStringValue(rhs);
|
|
388
548
|
if (create && create !== 1) {
|
|
389
549
|
if (lhs && !lhs[rhs]) {
|
|
@@ -397,10 +557,19 @@ ASTInterpreter.prototype = {
|
|
|
397
557
|
}
|
|
398
558
|
return value;
|
|
399
559
|
};
|
|
400
|
-
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Returns the value of a non-computed member expression.
|
|
564
|
+
* @param {function} left - The left operand function.
|
|
565
|
+
* @param {string} right - The right operand function.
|
|
566
|
+
* @param {Object} [context] - The context.
|
|
567
|
+
* @param {boolean|1} [create] - Whether to create the member if it does not exist.
|
|
568
|
+
* @returns {function} The function returning the non-computed member value.
|
|
569
|
+
*/
|
|
401
570
|
nonComputedMember(left, right, context, create) {
|
|
402
|
-
return function (scope, locals, assign
|
|
403
|
-
const lhs = left(scope, locals, assign
|
|
571
|
+
return function (scope, locals, assign) {
|
|
572
|
+
const lhs = left(scope, locals, assign);
|
|
404
573
|
if (create && create !== 1) {
|
|
405
574
|
if (lhs && lhs[right] == null) {
|
|
406
575
|
lhs[right] = {};
|
|
@@ -412,11 +581,307 @@ ASTInterpreter.prototype = {
|
|
|
412
581
|
}
|
|
413
582
|
return value;
|
|
414
583
|
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
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: "=",
|
|
420
808
|
};
|
|
421
|
-
}
|
|
422
|
-
}
|
|
809
|
+
}
|
|
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
|
+
}
|