@angular-wave/angular.ts 0.0.47 → 0.0.49
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/Makefile +3 -0
- package/README.md +1 -1
- package/css/angular.css +0 -6
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/jsdoc.json +24 -0
- package/package.json +6 -2
- package/src/angular.spec.js +1 -2
- package/src/animations/animate-queue.js +0 -1
- package/src/animations/animation.js +1 -1
- package/src/animations/raf-scheduler.js +0 -1
- package/src/animations/shared.js +1 -1
- package/src/core/animate/animate.js +0 -1
- package/src/core/compile/compile.md +1 -1
- package/src/core/compile/compile.spec.js +49 -47
- package/src/core/location/location.spec.js +1 -1
- package/src/core/on.spec.js +7 -12
- package/src/core/parser/ast-type.js +22 -0
- package/src/core/parser/ast.js +426 -0
- package/src/core/parser/compiler.js +561 -0
- package/src/core/parser/interpreter.js +422 -0
- package/src/core/parser/lexer.js +345 -0
- package/src/core/parser/parse.js +23 -1984
- package/src/core/parser/parse.md +57 -0
- package/src/core/parser/parse.spec.js +2 -2
- package/src/core/parser/parser.js +45 -0
- package/src/core/parser/shared.js +228 -0
- package/src/core/prop.spec.js +4 -4
- package/src/core/q/q.spec.js +0 -1
- package/src/core/sce/sce.js +3 -6
- package/src/core/scope/scope.js +33 -21
- package/src/core/task-tracker-factory.js +0 -1
- package/src/directive/class/class.js +0 -2
- package/src/directive/form/form.js +0 -3
- package/src/directive/form/form.spec.js +18 -18
- package/src/directive/include/include.js +1 -1
- package/src/directive/include/include.spec.js +18 -19
- package/src/directive/input/input.js +1 -2
- package/src/directive/model/model.js +1 -3
- package/src/directive/model/model.spec.js +0 -1
- package/src/directive/repeat/repeat.spec.js +0 -2
- package/src/directive/switch/switch.spec.js +4 -4
- package/src/exts/aria/aria.js +0 -1
- package/src/filters/filter.spec.js +0 -1
- package/src/injector.js +1 -1
- package/src/injector.spec.js +0 -5
- package/src/loader.js +0 -5
- package/src/services/cookie-reader.js +0 -1
- package/src/services/http/http.spec.js +0 -2
- package/src/shared/constants.js +3 -2
- package/src/shared/utils.js +18 -7
- package/src/types.js +10 -0
- package/types/core/parser/ast-type.d.ts +20 -0
- package/types/core/parser/ast.d.ts +86 -0
- package/types/core/parser/compiler.d.ts +49 -0
- package/types/core/parser/interpreter.d.ts +57 -0
- package/types/core/parser/lexer.d.ts +153 -0
- package/types/core/parser/parse.d.ts +68 -0
- package/types/core/parser/parser.d.ts +28 -0
- package/types/core/parser/shared.d.ts +29 -0
- package/types/core/scope/scope.d.ts +19 -12
- package/types/shared/utils.d.ts +18 -5
- package/types/types.d.ts +1 -0
- package/types-back/index.d.ts +0 -12
package/src/core/parser/parse.js
CHANGED
|
@@ -4,1974 +4,17 @@ import {
|
|
|
4
4
|
isDefined,
|
|
5
5
|
isFunction,
|
|
6
6
|
minErr,
|
|
7
|
-
isString,
|
|
8
|
-
isNumber,
|
|
9
7
|
} from "../../shared/utils";
|
|
8
|
+
import { getValueOf, PURITY_RELATIVE } from "./shared";
|
|
9
|
+
import { Lexer } from "./lexer";
|
|
10
|
+
import { Parser } from "./parser";
|
|
10
11
|
|
|
11
|
-
const $parseMinErr = minErr("$parse");
|
|
12
|
-
|
|
13
|
-
const objectValueOf = {}.constructor.prototype.valueOf;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Converts parameter to strings property name for use as keys in an object.
|
|
17
|
-
* Any non-string object, including a number, is typecasted into a string via the toString method.
|
|
18
|
-
* {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names}
|
|
19
|
-
*
|
|
20
|
-
* @param {!any} name
|
|
21
|
-
* @returns {string}
|
|
22
|
-
*/
|
|
23
|
-
function getStringValue(name) {
|
|
24
|
-
return `${name}`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const OPERATORS = Object.create(null);
|
|
28
|
-
|
|
29
|
-
"+ - * / % === !== == != < > <= >= && || ! = |"
|
|
30
|
-
.split(" ")
|
|
31
|
-
.forEach((operator) => (OPERATORS[operator] = true));
|
|
32
|
-
|
|
33
|
-
const ESCAPE = {
|
|
34
|
-
n: "\n",
|
|
35
|
-
f: "\f",
|
|
36
|
-
r: "\r",
|
|
37
|
-
t: "\t",
|
|
38
|
-
v: "\v",
|
|
39
|
-
"'": "'",
|
|
40
|
-
'"': '"',
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/// //////////////////////////////////////
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @constructor
|
|
47
|
-
*/
|
|
48
|
-
export const Lexer = function Lexer(options) {
|
|
49
|
-
this.options = options;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
Lexer.prototype = {
|
|
53
|
-
constructor: Lexer,
|
|
54
|
-
|
|
55
|
-
lex(text) {
|
|
56
|
-
this.text = text;
|
|
57
|
-
this.index = 0;
|
|
58
|
-
this.tokens = [];
|
|
59
|
-
|
|
60
|
-
while (this.index < this.text.length) {
|
|
61
|
-
const ch = this.text.charAt(this.index);
|
|
62
|
-
if (ch === '"' || ch === "'") {
|
|
63
|
-
this.readString(ch);
|
|
64
|
-
} else if (
|
|
65
|
-
this.isNumber(ch) ||
|
|
66
|
-
(ch === "." && this.isNumber(this.peek()))
|
|
67
|
-
) {
|
|
68
|
-
this.readNumber();
|
|
69
|
-
} else if (this.isIdentifierStart(this.peekMultichar())) {
|
|
70
|
-
this.readIdent();
|
|
71
|
-
} else if (this.is(ch, "(){}[].,;:?")) {
|
|
72
|
-
this.tokens.push({ index: this.index, text: ch });
|
|
73
|
-
this.index++;
|
|
74
|
-
} else if (this.isWhitespace(ch)) {
|
|
75
|
-
this.index++;
|
|
76
|
-
} else {
|
|
77
|
-
const ch2 = ch + this.peek();
|
|
78
|
-
const ch3 = ch2 + this.peek(2);
|
|
79
|
-
const op1 = OPERATORS[ch];
|
|
80
|
-
const op2 = OPERATORS[ch2];
|
|
81
|
-
const op3 = OPERATORS[ch3];
|
|
82
|
-
if (op1 || op2 || op3) {
|
|
83
|
-
const token = op3 ? ch3 : op2 ? ch2 : ch;
|
|
84
|
-
this.tokens.push({ index: this.index, text: token, operator: true });
|
|
85
|
-
this.index += token.length;
|
|
86
|
-
} else {
|
|
87
|
-
this.throwError(
|
|
88
|
-
"Unexpected next character ",
|
|
89
|
-
this.index,
|
|
90
|
-
this.index + 1,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return this.tokens;
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
is(ch, chars) {
|
|
99
|
-
return chars.indexOf(ch) !== -1;
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
peek(i) {
|
|
103
|
-
const num = i || 1;
|
|
104
|
-
return this.index + num < this.text.length
|
|
105
|
-
? this.text.charAt(this.index + num)
|
|
106
|
-
: false;
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
isNumber(ch) {
|
|
110
|
-
return ch >= "0" && ch <= "9" && typeof ch === "string";
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
isWhitespace(ch) {
|
|
114
|
-
// IE treats non-breaking space as \u00A0
|
|
115
|
-
return (
|
|
116
|
-
ch === " " ||
|
|
117
|
-
ch === "\r" ||
|
|
118
|
-
ch === "\t" ||
|
|
119
|
-
ch === "\n" ||
|
|
120
|
-
ch === "\v" ||
|
|
121
|
-
ch === "\u00A0"
|
|
122
|
-
);
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
isIdentifierStart(ch) {
|
|
126
|
-
return this.options.isIdentifierStart
|
|
127
|
-
? this.options.isIdentifierStart(ch, this.codePointAt(ch))
|
|
128
|
-
: this.isValidIdentifierStart(ch);
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
isValidIdentifierStart(ch) {
|
|
132
|
-
return (
|
|
133
|
-
(ch >= "a" && ch <= "z") ||
|
|
134
|
-
(ch >= "A" && ch <= "Z") ||
|
|
135
|
-
ch === "_" ||
|
|
136
|
-
ch === "$"
|
|
137
|
-
);
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
isIdentifierContinue(ch) {
|
|
141
|
-
return this.options.isIdentifierContinue
|
|
142
|
-
? this.options.isIdentifierContinue(ch, this.codePointAt(ch))
|
|
143
|
-
: this.isValidIdentifierContinue(ch);
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
isValidIdentifierContinue(ch) {
|
|
147
|
-
return this.isValidIdentifierStart(ch) || this.isNumber(ch);
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
codePointAt(ch) {
|
|
151
|
-
if (ch.length === 1) return ch.charCodeAt(0);
|
|
152
|
-
// eslint-disable-next-line no-bitwise
|
|
153
|
-
return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
peekMultichar() {
|
|
157
|
-
const ch = this.text.charAt(this.index);
|
|
158
|
-
const peek = this.peek();
|
|
159
|
-
if (!peek) {
|
|
160
|
-
return ch;
|
|
161
|
-
}
|
|
162
|
-
const cp1 = ch.charCodeAt(0);
|
|
163
|
-
const cp2 = peek.charCodeAt(0);
|
|
164
|
-
if (cp1 >= 0xd800 && cp1 <= 0xdbff && cp2 >= 0xdc00 && cp2 <= 0xdfff) {
|
|
165
|
-
return ch + peek;
|
|
166
|
-
}
|
|
167
|
-
return ch;
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
isExpOperator(ch) {
|
|
171
|
-
return ch === "-" || ch === "+" || this.isNumber(ch);
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
throwError(error, start, end) {
|
|
175
|
-
end = end || this.index;
|
|
176
|
-
const colStr = isDefined(start)
|
|
177
|
-
? `s ${start}-${this.index} [${this.text.substring(start, end)}]`
|
|
178
|
-
: ` ${end}`;
|
|
179
|
-
throw $parseMinErr(
|
|
180
|
-
"lexerr",
|
|
181
|
-
"Lexer Error: {0} at column{1} in expression [{2}].",
|
|
182
|
-
error,
|
|
183
|
-
colStr,
|
|
184
|
-
this.text,
|
|
185
|
-
);
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
readNumber() {
|
|
189
|
-
let number = "";
|
|
190
|
-
const start = this.index;
|
|
191
|
-
while (this.index < this.text.length) {
|
|
192
|
-
const ch = this.text.charAt(this.index).toLowerCase();
|
|
193
|
-
if (ch === "." || this.isNumber(ch)) {
|
|
194
|
-
number += ch;
|
|
195
|
-
} else {
|
|
196
|
-
const peekCh = this.peek();
|
|
197
|
-
if (ch === "e" && this.isExpOperator(peekCh)) {
|
|
198
|
-
number += ch;
|
|
199
|
-
} else if (
|
|
200
|
-
this.isExpOperator(ch) &&
|
|
201
|
-
peekCh &&
|
|
202
|
-
this.isNumber(peekCh) &&
|
|
203
|
-
number.charAt(number.length - 1) === "e"
|
|
204
|
-
) {
|
|
205
|
-
number += ch;
|
|
206
|
-
} else if (
|
|
207
|
-
this.isExpOperator(ch) &&
|
|
208
|
-
(!peekCh || !this.isNumber(peekCh)) &&
|
|
209
|
-
number.charAt(number.length - 1) === "e"
|
|
210
|
-
) {
|
|
211
|
-
this.throwError("Invalid exponent");
|
|
212
|
-
} else {
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
this.index++;
|
|
217
|
-
}
|
|
218
|
-
this.tokens.push({
|
|
219
|
-
index: start,
|
|
220
|
-
text: number,
|
|
221
|
-
constant: true,
|
|
222
|
-
value: Number(number),
|
|
223
|
-
});
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
readIdent() {
|
|
227
|
-
const start = this.index;
|
|
228
|
-
this.index += this.peekMultichar().length;
|
|
229
|
-
while (this.index < this.text.length) {
|
|
230
|
-
const ch = this.peekMultichar();
|
|
231
|
-
if (!this.isIdentifierContinue(ch)) {
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
this.index += ch.length;
|
|
235
|
-
}
|
|
236
|
-
this.tokens.push({
|
|
237
|
-
index: start,
|
|
238
|
-
text: this.text.slice(start, this.index),
|
|
239
|
-
identifier: true,
|
|
240
|
-
});
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
readString(quote) {
|
|
244
|
-
const start = this.index;
|
|
245
|
-
this.index++;
|
|
246
|
-
let string = "";
|
|
247
|
-
let rawString = quote;
|
|
248
|
-
let escape = false;
|
|
249
|
-
while (this.index < this.text.length) {
|
|
250
|
-
const ch = this.text.charAt(this.index);
|
|
251
|
-
rawString += ch;
|
|
252
|
-
if (escape) {
|
|
253
|
-
if (ch === "u") {
|
|
254
|
-
const hex = this.text.substring(this.index + 1, this.index + 5);
|
|
255
|
-
if (!hex.match(/[\da-f]{4}/i)) {
|
|
256
|
-
this.throwError(`Invalid unicode escape [\\u${hex}]`);
|
|
257
|
-
}
|
|
258
|
-
this.index += 4;
|
|
259
|
-
string += String.fromCharCode(parseInt(hex, 16));
|
|
260
|
-
} else {
|
|
261
|
-
const rep = ESCAPE[ch];
|
|
262
|
-
string += rep || ch;
|
|
263
|
-
}
|
|
264
|
-
escape = false;
|
|
265
|
-
} else if (ch === "\\") {
|
|
266
|
-
escape = true;
|
|
267
|
-
} else if (ch === quote) {
|
|
268
|
-
this.index++;
|
|
269
|
-
this.tokens.push({
|
|
270
|
-
index: start,
|
|
271
|
-
text: rawString,
|
|
272
|
-
constant: true,
|
|
273
|
-
value: string,
|
|
274
|
-
});
|
|
275
|
-
return;
|
|
276
|
-
} else {
|
|
277
|
-
string += ch;
|
|
278
|
-
}
|
|
279
|
-
this.index++;
|
|
280
|
-
}
|
|
281
|
-
this.throwError("Unterminated quote", start);
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* @typedef {("Program"|"ExpressionStatement"|"AssignmentExpression"|"ConditionalExpression"|"LogicalExpression"|"BinaryExpression"|"UnaryExpression"|"CallExpression"|"MemberExpression"|"Identifier"|"Literal"|"ArrayExpression"|"Property"|"ObjectExpression"|"ThisExpression"|"LocalsExpression"|"NGValueParameter")} ASTType
|
|
287
|
-
*/
|
|
288
|
-
const ASTType = {
|
|
289
|
-
Program: "Program",
|
|
290
|
-
ExpressionStatement: "ExpressionStatement",
|
|
291
|
-
AssignmentExpression: "AssignmentExpression",
|
|
292
|
-
ConditionalExpression: "ConditionalExpression",
|
|
293
|
-
LogicalExpression: "LogicalExpression",
|
|
294
|
-
BinaryExpression: "BinaryExpression",
|
|
295
|
-
UnaryExpression: "UnaryExpression",
|
|
296
|
-
CallExpression: "CallExpression",
|
|
297
|
-
MemberExpression: "MemberExpression",
|
|
298
|
-
Identifier: "Identifier",
|
|
299
|
-
Literal: "Literal",
|
|
300
|
-
ArrayExpression: "ArrayExpression",
|
|
301
|
-
Property: "Property",
|
|
302
|
-
ObjectExpression: "ObjectExpression",
|
|
303
|
-
ThisExpression: "ThisExpression",
|
|
304
|
-
LocalsExpression: "LocalsExpression",
|
|
305
|
-
NGValueParameter: "NGValueParameter",
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
export function AST(lexer, options) {
|
|
309
|
-
this.lexer = lexer;
|
|
310
|
-
this.options = options;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
AST.prototype = {
|
|
314
|
-
ast(text) {
|
|
315
|
-
this.text = text;
|
|
316
|
-
this.tokens = this.lexer.lex(text);
|
|
317
|
-
|
|
318
|
-
const value = this.program();
|
|
319
|
-
|
|
320
|
-
if (this.tokens.length !== 0) {
|
|
321
|
-
this.throwError("is an unexpected token", this.tokens[0]);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return value;
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
program() {
|
|
328
|
-
const body = [];
|
|
329
|
-
let hasMore = true;
|
|
330
|
-
while (hasMore) {
|
|
331
|
-
if (this.tokens.length > 0 && !this.peek("}", ")", ";", "]"))
|
|
332
|
-
body.push(this.expressionStatement());
|
|
333
|
-
if (!this.expect(";")) {
|
|
334
|
-
hasMore = false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return { type: ASTType.Program, body };
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
expressionStatement() {
|
|
341
|
-
return {
|
|
342
|
-
type: ASTType.ExpressionStatement,
|
|
343
|
-
expression: this.filterChain(),
|
|
344
|
-
};
|
|
345
|
-
},
|
|
346
|
-
|
|
347
|
-
filterChain() {
|
|
348
|
-
let left = this.expression();
|
|
349
|
-
while (this.expect("|")) {
|
|
350
|
-
left = this.filter(left);
|
|
351
|
-
}
|
|
352
|
-
return left;
|
|
353
|
-
},
|
|
354
|
-
|
|
355
|
-
expression() {
|
|
356
|
-
return this.assignment();
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
assignment() {
|
|
360
|
-
let result = this.ternary();
|
|
361
|
-
if (this.expect("=")) {
|
|
362
|
-
if (!isAssignable(result)) {
|
|
363
|
-
throw $parseMinErr("lval", "Trying to assign a value to a non l-value");
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
result = {
|
|
367
|
-
type: ASTType.AssignmentExpression,
|
|
368
|
-
left: result,
|
|
369
|
-
right: this.assignment(),
|
|
370
|
-
operator: "=",
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
return result;
|
|
374
|
-
},
|
|
375
|
-
|
|
376
|
-
ternary() {
|
|
377
|
-
const test = this.logicalOR();
|
|
378
|
-
let alternate;
|
|
379
|
-
let consequent;
|
|
380
|
-
if (this.expect("?")) {
|
|
381
|
-
alternate = this.expression();
|
|
382
|
-
if (this.consume(":")) {
|
|
383
|
-
consequent = this.expression();
|
|
384
|
-
return {
|
|
385
|
-
type: ASTType.ConditionalExpression,
|
|
386
|
-
test,
|
|
387
|
-
alternate,
|
|
388
|
-
consequent,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return test;
|
|
393
|
-
},
|
|
394
|
-
|
|
395
|
-
logicalOR() {
|
|
396
|
-
let left = this.logicalAND();
|
|
397
|
-
while (this.expect("||")) {
|
|
398
|
-
left = {
|
|
399
|
-
type: ASTType.LogicalExpression,
|
|
400
|
-
operator: "||",
|
|
401
|
-
left,
|
|
402
|
-
right: this.logicalAND(),
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
return left;
|
|
406
|
-
},
|
|
407
|
-
|
|
408
|
-
logicalAND() {
|
|
409
|
-
let left = this.equality();
|
|
410
|
-
while (this.expect("&&")) {
|
|
411
|
-
left = {
|
|
412
|
-
type: ASTType.LogicalExpression,
|
|
413
|
-
operator: "&&",
|
|
414
|
-
left,
|
|
415
|
-
right: this.equality(),
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
return left;
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
equality() {
|
|
422
|
-
let left = this.relational();
|
|
423
|
-
let token;
|
|
424
|
-
while ((token = this.expect("==", "!=", "===", "!=="))) {
|
|
425
|
-
left = {
|
|
426
|
-
type: ASTType.BinaryExpression,
|
|
427
|
-
operator: token.text,
|
|
428
|
-
left,
|
|
429
|
-
right: this.relational(),
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
return left;
|
|
433
|
-
},
|
|
434
|
-
|
|
435
|
-
relational() {
|
|
436
|
-
let left = this.additive();
|
|
437
|
-
let token;
|
|
438
|
-
while ((token = this.expect("<", ">", "<=", ">="))) {
|
|
439
|
-
left = {
|
|
440
|
-
type: ASTType.BinaryExpression,
|
|
441
|
-
operator: token.text,
|
|
442
|
-
left,
|
|
443
|
-
right: this.additive(),
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
return left;
|
|
447
|
-
},
|
|
448
|
-
|
|
449
|
-
additive() {
|
|
450
|
-
let left = this.multiplicative();
|
|
451
|
-
let token;
|
|
452
|
-
while ((token = this.expect("+", "-"))) {
|
|
453
|
-
left = {
|
|
454
|
-
type: ASTType.BinaryExpression,
|
|
455
|
-
operator: token.text,
|
|
456
|
-
left,
|
|
457
|
-
right: this.multiplicative(),
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
return left;
|
|
461
|
-
},
|
|
462
|
-
|
|
463
|
-
multiplicative() {
|
|
464
|
-
let left = this.unary();
|
|
465
|
-
let token;
|
|
466
|
-
while ((token = this.expect("*", "/", "%"))) {
|
|
467
|
-
left = {
|
|
468
|
-
type: ASTType.BinaryExpression,
|
|
469
|
-
operator: token.text,
|
|
470
|
-
left,
|
|
471
|
-
right: this.unary(),
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
return left;
|
|
475
|
-
},
|
|
476
|
-
|
|
477
|
-
unary() {
|
|
478
|
-
let token;
|
|
479
|
-
if ((token = this.expect("+", "-", "!"))) {
|
|
480
|
-
return {
|
|
481
|
-
type: ASTType.UnaryExpression,
|
|
482
|
-
operator: token.text,
|
|
483
|
-
prefix: true,
|
|
484
|
-
argument: this.unary(),
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
return this.primary();
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
primary() {
|
|
491
|
-
let primary;
|
|
492
|
-
if (this.expect("(")) {
|
|
493
|
-
primary = this.filterChain();
|
|
494
|
-
this.consume(")");
|
|
495
|
-
} else if (this.expect("[")) {
|
|
496
|
-
primary = this.arrayDeclaration();
|
|
497
|
-
} else if (this.expect("{")) {
|
|
498
|
-
primary = this.object();
|
|
499
|
-
} else if (
|
|
500
|
-
Object.prototype.hasOwnProperty.call(
|
|
501
|
-
this.selfReferential,
|
|
502
|
-
this.peek().text,
|
|
503
|
-
)
|
|
504
|
-
) {
|
|
505
|
-
primary = structuredClone(this.selfReferential[this.consume().text]);
|
|
506
|
-
} else if (
|
|
507
|
-
Object.prototype.hasOwnProperty.call(
|
|
508
|
-
this.options.literals,
|
|
509
|
-
this.peek().text,
|
|
510
|
-
)
|
|
511
|
-
) {
|
|
512
|
-
primary = {
|
|
513
|
-
type: ASTType.Literal,
|
|
514
|
-
value: this.options.literals[this.consume().text],
|
|
515
|
-
};
|
|
516
|
-
} else if (this.peek().identifier) {
|
|
517
|
-
primary = this.identifier();
|
|
518
|
-
} else if (this.peek().constant) {
|
|
519
|
-
primary = this.constant();
|
|
520
|
-
} else {
|
|
521
|
-
this.throwError("not a primary expression", this.peek());
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
let next;
|
|
525
|
-
while ((next = this.expect("(", "[", "."))) {
|
|
526
|
-
if (next.text === "(") {
|
|
527
|
-
primary = {
|
|
528
|
-
type: ASTType.CallExpression,
|
|
529
|
-
callee: primary,
|
|
530
|
-
arguments: this.parseArguments(),
|
|
531
|
-
};
|
|
532
|
-
this.consume(")");
|
|
533
|
-
} else if (next.text === "[") {
|
|
534
|
-
primary = {
|
|
535
|
-
type: ASTType.MemberExpression,
|
|
536
|
-
object: primary,
|
|
537
|
-
property: this.expression(),
|
|
538
|
-
computed: true,
|
|
539
|
-
};
|
|
540
|
-
this.consume("]");
|
|
541
|
-
} else if (next.text === ".") {
|
|
542
|
-
primary = {
|
|
543
|
-
type: ASTType.MemberExpression,
|
|
544
|
-
object: primary,
|
|
545
|
-
property: this.identifier(),
|
|
546
|
-
computed: false,
|
|
547
|
-
};
|
|
548
|
-
} else {
|
|
549
|
-
this.throwError("IMPOSSIBLE");
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return primary;
|
|
553
|
-
},
|
|
554
|
-
|
|
555
|
-
filter(baseExpression) {
|
|
556
|
-
const args = [baseExpression];
|
|
557
|
-
const result = {
|
|
558
|
-
type: ASTType.CallExpression,
|
|
559
|
-
callee: this.identifier(),
|
|
560
|
-
arguments: args,
|
|
561
|
-
filter: true,
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
while (this.expect(":")) {
|
|
565
|
-
args.push(this.expression());
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
return result;
|
|
569
|
-
},
|
|
570
|
-
|
|
571
|
-
parseArguments() {
|
|
572
|
-
const args = [];
|
|
573
|
-
if (this.peekToken().text !== ")") {
|
|
574
|
-
do {
|
|
575
|
-
args.push(this.filterChain());
|
|
576
|
-
} while (this.expect(","));
|
|
577
|
-
}
|
|
578
|
-
return args;
|
|
579
|
-
},
|
|
580
|
-
|
|
581
|
-
identifier() {
|
|
582
|
-
const token = this.consume();
|
|
583
|
-
if (!token.identifier) {
|
|
584
|
-
this.throwError("is not a valid identifier", token);
|
|
585
|
-
}
|
|
586
|
-
return { type: ASTType.Identifier, name: token.text };
|
|
587
|
-
},
|
|
588
|
-
|
|
589
|
-
constant() {
|
|
590
|
-
// TODO check that it is a constant
|
|
591
|
-
return { type: ASTType.Literal, value: this.consume().value };
|
|
592
|
-
},
|
|
593
|
-
|
|
594
|
-
arrayDeclaration() {
|
|
595
|
-
const elements = [];
|
|
596
|
-
if (this.peekToken().text !== "]") {
|
|
597
|
-
do {
|
|
598
|
-
if (this.peek("]")) {
|
|
599
|
-
// Support trailing commas per ES5.1.
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
elements.push(this.expression());
|
|
603
|
-
} while (this.expect(","));
|
|
604
|
-
}
|
|
605
|
-
this.consume("]");
|
|
606
|
-
|
|
607
|
-
return { type: ASTType.ArrayExpression, elements };
|
|
608
|
-
},
|
|
609
|
-
|
|
610
|
-
object() {
|
|
611
|
-
const properties = [];
|
|
612
|
-
let property;
|
|
613
|
-
if (this.peekToken().text !== "}") {
|
|
614
|
-
do {
|
|
615
|
-
if (this.peek("}")) {
|
|
616
|
-
// Support trailing commas per ES5.1.
|
|
617
|
-
break;
|
|
618
|
-
}
|
|
619
|
-
property = { type: ASTType.Property, kind: "init" };
|
|
620
|
-
if (this.peek().constant) {
|
|
621
|
-
property.key = this.constant();
|
|
622
|
-
property.computed = false;
|
|
623
|
-
this.consume(":");
|
|
624
|
-
property.value = this.expression();
|
|
625
|
-
} else if (this.peek().identifier) {
|
|
626
|
-
property.key = this.identifier();
|
|
627
|
-
property.computed = false;
|
|
628
|
-
if (this.peek(":")) {
|
|
629
|
-
this.consume(":");
|
|
630
|
-
property.value = this.expression();
|
|
631
|
-
} else {
|
|
632
|
-
property.value = property.key;
|
|
633
|
-
}
|
|
634
|
-
} else if (this.peek("[")) {
|
|
635
|
-
this.consume("[");
|
|
636
|
-
property.key = this.expression();
|
|
637
|
-
this.consume("]");
|
|
638
|
-
property.computed = true;
|
|
639
|
-
this.consume(":");
|
|
640
|
-
property.value = this.expression();
|
|
641
|
-
} else {
|
|
642
|
-
this.throwError("invalid key", this.peek());
|
|
643
|
-
}
|
|
644
|
-
properties.push(property);
|
|
645
|
-
} while (this.expect(","));
|
|
646
|
-
}
|
|
647
|
-
this.consume("}");
|
|
648
|
-
|
|
649
|
-
return { type: ASTType.ObjectExpression, properties };
|
|
650
|
-
},
|
|
651
|
-
|
|
652
|
-
throwError(msg, token) {
|
|
653
|
-
throw $parseMinErr(
|
|
654
|
-
"syntax",
|
|
655
|
-
"Syntax Error: Token '{0}' {1} at column {2} of the expression [{3}] starting at [{4}].",
|
|
656
|
-
token.text,
|
|
657
|
-
msg,
|
|
658
|
-
token.index + 1,
|
|
659
|
-
this.text,
|
|
660
|
-
this.text.substring(token.index),
|
|
661
|
-
);
|
|
662
|
-
},
|
|
663
|
-
|
|
664
|
-
consume(e1) {
|
|
665
|
-
if (this.tokens.length === 0) {
|
|
666
|
-
throw $parseMinErr(
|
|
667
|
-
"ueoe",
|
|
668
|
-
"Unexpected end of expression: {0}",
|
|
669
|
-
this.text,
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const token = this.expect(e1);
|
|
674
|
-
if (!token) {
|
|
675
|
-
this.throwError(`is unexpected, expecting [${e1}]`, this.peek());
|
|
676
|
-
}
|
|
677
|
-
return token;
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
peekToken() {
|
|
681
|
-
if (this.tokens.length === 0) {
|
|
682
|
-
throw $parseMinErr(
|
|
683
|
-
"ueoe",
|
|
684
|
-
"Unexpected end of expression: {0}",
|
|
685
|
-
this.text,
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
return this.tokens[0];
|
|
689
|
-
},
|
|
690
|
-
|
|
691
|
-
peek(e1, e2, e3, e4) {
|
|
692
|
-
return this.peekAhead(0, e1, e2, e3, e4);
|
|
693
|
-
},
|
|
694
|
-
|
|
695
|
-
peekAhead(i, e1, e2, e3, e4) {
|
|
696
|
-
if (this.tokens.length > i) {
|
|
697
|
-
const token = this.tokens[i];
|
|
698
|
-
const t = token.text;
|
|
699
|
-
if (
|
|
700
|
-
t === e1 ||
|
|
701
|
-
t === e2 ||
|
|
702
|
-
t === e3 ||
|
|
703
|
-
t === e4 ||
|
|
704
|
-
(!e1 && !e2 && !e3 && !e4)
|
|
705
|
-
) {
|
|
706
|
-
return token;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
return false;
|
|
710
|
-
},
|
|
711
|
-
|
|
712
|
-
expect(e1, e2, e3, e4) {
|
|
713
|
-
const token = this.peek(e1, e2, e3, e4);
|
|
714
|
-
if (token) {
|
|
715
|
-
this.tokens.shift();
|
|
716
|
-
return token;
|
|
717
|
-
}
|
|
718
|
-
return false;
|
|
719
|
-
},
|
|
720
|
-
|
|
721
|
-
selfReferential: {
|
|
722
|
-
this: { type: ASTType.ThisExpression },
|
|
723
|
-
$locals: { type: ASTType.LocalsExpression },
|
|
724
|
-
},
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
function ifDefined(v, d) {
|
|
728
|
-
return typeof v !== "undefined" ? v : d;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
function plusFn(l, r) {
|
|
732
|
-
if (typeof l === "undefined") return r;
|
|
733
|
-
if (typeof r === "undefined") return l;
|
|
734
|
-
return l + r;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function isStateless($filter, filterName) {
|
|
738
|
-
const fn = $filter(filterName);
|
|
739
|
-
return !fn.$stateful;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
const PURITY_ABSOLUTE = 1;
|
|
743
|
-
const PURITY_RELATIVE = 2;
|
|
744
|
-
|
|
745
|
-
// Detect nodes which could depend on non-shallow state of objects
|
|
746
|
-
function isPure(node, parentIsPure) {
|
|
747
|
-
switch (node.type) {
|
|
748
|
-
// Computed members might invoke a stateful toString()
|
|
749
|
-
case ASTType.MemberExpression:
|
|
750
|
-
if (node.computed) {
|
|
751
|
-
return false;
|
|
752
|
-
}
|
|
753
|
-
break;
|
|
754
|
-
|
|
755
|
-
// Unary always convert to primative
|
|
756
|
-
case ASTType.UnaryExpression:
|
|
757
|
-
return PURITY_ABSOLUTE;
|
|
758
|
-
|
|
759
|
-
// The binary + operator can invoke a stateful toString().
|
|
760
|
-
case ASTType.BinaryExpression:
|
|
761
|
-
return node.operator !== "+" ? PURITY_ABSOLUTE : false;
|
|
762
|
-
|
|
763
|
-
// Functions / filters probably read state from within objects
|
|
764
|
-
case ASTType.CallExpression:
|
|
765
|
-
return false;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
|
772
|
-
let allConstants;
|
|
773
|
-
let argsToWatch;
|
|
774
|
-
let isStatelessFilter;
|
|
775
|
-
|
|
776
|
-
const astIsPure = (ast.isPure = isPure(ast, parentIsPure));
|
|
777
|
-
|
|
778
|
-
switch (ast.type) {
|
|
779
|
-
case ASTType.Program:
|
|
780
|
-
allConstants = true;
|
|
781
|
-
/** @type {[any]} */ (ast.body).forEach((expr) => {
|
|
782
|
-
findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
|
|
783
|
-
allConstants = allConstants && expr.expression.constant;
|
|
784
|
-
});
|
|
785
|
-
ast.constant = allConstants;
|
|
786
|
-
break;
|
|
787
|
-
case ASTType.Literal:
|
|
788
|
-
ast.constant = true;
|
|
789
|
-
ast.toWatch = [];
|
|
790
|
-
break;
|
|
791
|
-
case ASTType.UnaryExpression:
|
|
792
|
-
findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
|
|
793
|
-
ast.constant = ast.argument.constant;
|
|
794
|
-
ast.toWatch = ast.argument.toWatch;
|
|
795
|
-
break;
|
|
796
|
-
case ASTType.BinaryExpression:
|
|
797
|
-
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
|
798
|
-
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
|
799
|
-
ast.constant = ast.left.constant && ast.right.constant;
|
|
800
|
-
ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
|
|
801
|
-
break;
|
|
802
|
-
case ASTType.LogicalExpression:
|
|
803
|
-
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
|
804
|
-
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
|
805
|
-
ast.constant = ast.left.constant && ast.right.constant;
|
|
806
|
-
ast.toWatch = ast.constant ? [] : [ast];
|
|
807
|
-
break;
|
|
808
|
-
case ASTType.ConditionalExpression:
|
|
809
|
-
findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
|
|
810
|
-
findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
|
|
811
|
-
findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
|
|
812
|
-
ast.constant =
|
|
813
|
-
ast.test.constant && ast.alternate.constant && ast.consequent.constant;
|
|
814
|
-
ast.toWatch = ast.constant ? [] : [ast];
|
|
815
|
-
break;
|
|
816
|
-
case ASTType.Identifier:
|
|
817
|
-
ast.constant = false;
|
|
818
|
-
ast.toWatch = [ast];
|
|
819
|
-
break;
|
|
820
|
-
case ASTType.MemberExpression:
|
|
821
|
-
findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
|
|
822
|
-
if (ast.computed) {
|
|
823
|
-
findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
|
|
824
|
-
}
|
|
825
|
-
ast.constant =
|
|
826
|
-
ast.object.constant && (!ast.computed || ast.property.constant);
|
|
827
|
-
ast.toWatch = ast.constant ? [] : [ast];
|
|
828
|
-
break;
|
|
829
|
-
case ASTType.CallExpression:
|
|
830
|
-
isStatelessFilter = ast.filter
|
|
831
|
-
? isStateless($filter, ast.callee.name)
|
|
832
|
-
: false;
|
|
833
|
-
allConstants = isStatelessFilter;
|
|
834
|
-
argsToWatch = [];
|
|
835
|
-
forEach(ast.arguments, (expr) => {
|
|
836
|
-
findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
|
837
|
-
allConstants = allConstants && expr.constant;
|
|
838
|
-
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
|
839
|
-
});
|
|
840
|
-
ast.constant = allConstants;
|
|
841
|
-
ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
|
|
842
|
-
break;
|
|
843
|
-
case ASTType.AssignmentExpression:
|
|
844
|
-
findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
|
|
845
|
-
findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
|
|
846
|
-
ast.constant = ast.left.constant && ast.right.constant;
|
|
847
|
-
ast.toWatch = [ast];
|
|
848
|
-
break;
|
|
849
|
-
case ASTType.ArrayExpression:
|
|
850
|
-
allConstants = true;
|
|
851
|
-
argsToWatch = [];
|
|
852
|
-
forEach(ast.elements, (expr) => {
|
|
853
|
-
findConstantAndWatchExpressions(expr, $filter, astIsPure);
|
|
854
|
-
allConstants = allConstants && expr.constant;
|
|
855
|
-
argsToWatch.push.apply(argsToWatch, expr.toWatch);
|
|
856
|
-
});
|
|
857
|
-
ast.constant = allConstants;
|
|
858
|
-
ast.toWatch = argsToWatch;
|
|
859
|
-
break;
|
|
860
|
-
case ASTType.ObjectExpression:
|
|
861
|
-
allConstants = true;
|
|
862
|
-
argsToWatch = [];
|
|
863
|
-
forEach(ast.properties, (property) => {
|
|
864
|
-
findConstantAndWatchExpressions(property.value, $filter, astIsPure);
|
|
865
|
-
allConstants = allConstants && property.value.constant;
|
|
866
|
-
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
|
|
867
|
-
if (property.computed) {
|
|
868
|
-
// `{[key]: value}` implicitly does `key.toString()` which may be non-pure
|
|
869
|
-
findConstantAndWatchExpressions(
|
|
870
|
-
property.key,
|
|
871
|
-
$filter,
|
|
872
|
-
/* parentIsPure= */ false,
|
|
873
|
-
);
|
|
874
|
-
allConstants = allConstants && property.key.constant;
|
|
875
|
-
argsToWatch.push.apply(argsToWatch, property.key.toWatch);
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
ast.constant = allConstants;
|
|
879
|
-
ast.toWatch = argsToWatch;
|
|
880
|
-
break;
|
|
881
|
-
case ASTType.ThisExpression:
|
|
882
|
-
ast.constant = false;
|
|
883
|
-
ast.toWatch = [];
|
|
884
|
-
break;
|
|
885
|
-
case ASTType.LocalsExpression:
|
|
886
|
-
ast.constant = false;
|
|
887
|
-
ast.toWatch = [];
|
|
888
|
-
break;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
function getInputs(body) {
|
|
893
|
-
if (body.length !== 1) return;
|
|
894
|
-
const lastExpression = body[0].expression;
|
|
895
|
-
const candidate = lastExpression.toWatch;
|
|
896
|
-
if (candidate.length !== 1) return candidate;
|
|
897
|
-
return candidate[0] !== lastExpression ? candidate : undefined;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
function isAssignable(ast) {
|
|
901
|
-
return (
|
|
902
|
-
ast.type === ASTType.Identifier || ast.type === ASTType.MemberExpression
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
function assignableAST(ast) {
|
|
907
|
-
if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
|
|
908
|
-
return {
|
|
909
|
-
type: ASTType.AssignmentExpression,
|
|
910
|
-
left: ast.body[0].expression,
|
|
911
|
-
right: { type: ASTType.NGValueParameter },
|
|
912
|
-
operator: "=",
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
function isLiteral(ast) {
|
|
918
|
-
return (
|
|
919
|
-
ast.body.length === 0 ||
|
|
920
|
-
(ast.body.length === 1 &&
|
|
921
|
-
(ast.body[0].expression.type === ASTType.Literal ||
|
|
922
|
-
ast.body[0].expression.type === ASTType.ArrayExpression ||
|
|
923
|
-
ast.body[0].expression.type === ASTType.ObjectExpression))
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function isConstant(ast) {
|
|
928
|
-
return ast.constant;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
function ASTCompiler($filter) {
|
|
932
|
-
this.$filter = $filter;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
ASTCompiler.prototype = {
|
|
936
|
-
compile(ast) {
|
|
937
|
-
const self = this;
|
|
938
|
-
this.state = {
|
|
939
|
-
nextId: 0,
|
|
940
|
-
filters: {},
|
|
941
|
-
fn: { vars: [], body: [], own: {} },
|
|
942
|
-
assign: { vars: [], body: [], own: {} },
|
|
943
|
-
inputs: [],
|
|
944
|
-
};
|
|
945
|
-
findConstantAndWatchExpressions(ast, self.$filter);
|
|
946
|
-
let extra = "";
|
|
947
|
-
let assignable;
|
|
948
|
-
this.stage = "assign";
|
|
949
|
-
if ((assignable = assignableAST(ast))) {
|
|
950
|
-
this.state.computing = "assign";
|
|
951
|
-
const result = this.nextId();
|
|
952
|
-
this.recurse(assignable, result);
|
|
953
|
-
this.return_(result);
|
|
954
|
-
extra = `fn.assign=${this.generateFunction("assign", "s,v,l")}`;
|
|
955
|
-
}
|
|
956
|
-
const toWatch = getInputs(ast.body);
|
|
957
|
-
self.stage = "inputs";
|
|
958
|
-
forEach(toWatch, (watch, key) => {
|
|
959
|
-
const fnKey = `fn${key}`;
|
|
960
|
-
self.state[fnKey] = { vars: [], body: [], own: {} };
|
|
961
|
-
self.state.computing = fnKey;
|
|
962
|
-
const intoId = self.nextId();
|
|
963
|
-
self.recurse(watch, intoId);
|
|
964
|
-
self.return_(intoId);
|
|
965
|
-
self.state.inputs.push({ name: fnKey, isPure: watch.isPure });
|
|
966
|
-
watch.watchId = key;
|
|
967
|
-
});
|
|
968
|
-
this.state.computing = "fn";
|
|
969
|
-
this.stage = "main";
|
|
970
|
-
this.recurse(ast);
|
|
971
|
-
const fnString = `\n${this.filterPrefix()}let fn=${this.generateFunction(
|
|
972
|
-
"fn",
|
|
973
|
-
"s,l,a,i",
|
|
974
|
-
)}${extra}${this.watchFns()}return fn;`;
|
|
975
|
-
|
|
976
|
-
// eslint-disable-next-line no-new-func
|
|
977
|
-
const fn = new Function(
|
|
978
|
-
"$filter",
|
|
979
|
-
"getStringValue",
|
|
980
|
-
"ifDefined",
|
|
981
|
-
"plus",
|
|
982
|
-
fnString,
|
|
983
|
-
)(this.$filter, getStringValue, ifDefined, plusFn);
|
|
984
|
-
this.state = this.stage = undefined;
|
|
985
|
-
return fn;
|
|
986
|
-
},
|
|
987
|
-
|
|
988
|
-
watchFns() {
|
|
989
|
-
const result = [];
|
|
990
|
-
const { inputs } = this.state;
|
|
991
|
-
const self = this;
|
|
992
|
-
forEach(inputs, (input) => {
|
|
993
|
-
result.push(
|
|
994
|
-
`let ${input.name}=${self.generateFunction(input.name, "s")}`,
|
|
995
|
-
);
|
|
996
|
-
if (input.isPure) {
|
|
997
|
-
result.push(input.name, `.isPure=${JSON.stringify(input.isPure)};`);
|
|
998
|
-
}
|
|
999
|
-
});
|
|
1000
|
-
if (inputs.length) {
|
|
1001
|
-
result.push(`fn.inputs=[${inputs.map((i) => i.name).join(",")}];`);
|
|
1002
|
-
}
|
|
1003
|
-
return result.join("");
|
|
1004
|
-
},
|
|
1005
|
-
|
|
1006
|
-
generateFunction(name, params) {
|
|
1007
|
-
return `function(${params}){${this.varsPrefix(name)}${this.body(name)}};`;
|
|
1008
|
-
},
|
|
1009
|
-
|
|
1010
|
-
filterPrefix() {
|
|
1011
|
-
const parts = [];
|
|
1012
|
-
const self = this;
|
|
1013
|
-
forEach(this.state.filters, (id, filter) => {
|
|
1014
|
-
parts.push(`${id}=$filter(${self.escape(filter)})`);
|
|
1015
|
-
});
|
|
1016
|
-
if (parts.length) return `let ${parts.join(",")};`;
|
|
1017
|
-
return "";
|
|
1018
|
-
},
|
|
1019
|
-
|
|
1020
|
-
varsPrefix(section) {
|
|
1021
|
-
return this.state[section].vars.length
|
|
1022
|
-
? `let ${this.state[section].vars.join(",")};`
|
|
1023
|
-
: "";
|
|
1024
|
-
},
|
|
1025
|
-
|
|
1026
|
-
body(section) {
|
|
1027
|
-
return this.state[section].body.join("");
|
|
1028
|
-
},
|
|
1029
|
-
|
|
1030
|
-
recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
|
|
1031
|
-
let left;
|
|
1032
|
-
let right;
|
|
1033
|
-
const self = this;
|
|
1034
|
-
let args;
|
|
1035
|
-
let expression;
|
|
1036
|
-
let computed;
|
|
1037
|
-
recursionFn = recursionFn || (() => {});
|
|
1038
|
-
if (!skipWatchIdCheck && isDefined(ast.watchId)) {
|
|
1039
|
-
intoId = intoId || this.nextId();
|
|
1040
|
-
this.if_(
|
|
1041
|
-
"i",
|
|
1042
|
-
this.lazyAssign(intoId, this.computedMember("i", ast.watchId)),
|
|
1043
|
-
this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true),
|
|
1044
|
-
);
|
|
1045
|
-
return;
|
|
1046
|
-
}
|
|
1047
|
-
switch (ast.type) {
|
|
1048
|
-
case ASTType.Program:
|
|
1049
|
-
forEach(ast.body, (expression, pos) => {
|
|
1050
|
-
self.recurse(expression.expression, undefined, undefined, (expr) => {
|
|
1051
|
-
right = expr;
|
|
1052
|
-
});
|
|
1053
|
-
if (pos !== ast.body.length - 1) {
|
|
1054
|
-
self.current().body.push(right, ";");
|
|
1055
|
-
} else {
|
|
1056
|
-
self.return_(right);
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
break;
|
|
1060
|
-
case ASTType.Literal:
|
|
1061
|
-
expression = this.escape(ast.value);
|
|
1062
|
-
this.assign(intoId, expression);
|
|
1063
|
-
recursionFn(intoId || expression);
|
|
1064
|
-
break;
|
|
1065
|
-
case ASTType.UnaryExpression:
|
|
1066
|
-
this.recurse(ast.argument, undefined, undefined, (expr) => {
|
|
1067
|
-
right = expr;
|
|
1068
|
-
});
|
|
1069
|
-
expression = `${ast.operator}(${this.ifDefined(right, 0)})`;
|
|
1070
|
-
this.assign(intoId, expression);
|
|
1071
|
-
recursionFn(expression);
|
|
1072
|
-
break;
|
|
1073
|
-
case ASTType.BinaryExpression:
|
|
1074
|
-
this.recurse(ast.left, undefined, undefined, (expr) => {
|
|
1075
|
-
left = expr;
|
|
1076
|
-
});
|
|
1077
|
-
this.recurse(ast.right, undefined, undefined, (expr) => {
|
|
1078
|
-
right = expr;
|
|
1079
|
-
});
|
|
1080
|
-
if (ast.operator === "+") {
|
|
1081
|
-
expression = this.plus(left, right);
|
|
1082
|
-
} else if (ast.operator === "-") {
|
|
1083
|
-
expression =
|
|
1084
|
-
this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
|
|
1085
|
-
} else {
|
|
1086
|
-
expression = `(${left})${ast.operator}(${right})`;
|
|
1087
|
-
}
|
|
1088
|
-
this.assign(intoId, expression);
|
|
1089
|
-
recursionFn(expression);
|
|
1090
|
-
break;
|
|
1091
|
-
case ASTType.LogicalExpression:
|
|
1092
|
-
intoId = intoId || this.nextId();
|
|
1093
|
-
self.recurse(ast.left, intoId);
|
|
1094
|
-
self.if_(
|
|
1095
|
-
ast.operator === "&&" ? intoId : self.not(intoId),
|
|
1096
|
-
self.lazyRecurse(ast.right, intoId),
|
|
1097
|
-
);
|
|
1098
|
-
recursionFn(intoId);
|
|
1099
|
-
break;
|
|
1100
|
-
case ASTType.ConditionalExpression:
|
|
1101
|
-
intoId = intoId || this.nextId();
|
|
1102
|
-
self.recurse(ast.test, intoId);
|
|
1103
|
-
self.if_(
|
|
1104
|
-
intoId,
|
|
1105
|
-
self.lazyRecurse(ast.alternate, intoId),
|
|
1106
|
-
self.lazyRecurse(ast.consequent, intoId),
|
|
1107
|
-
);
|
|
1108
|
-
recursionFn(intoId);
|
|
1109
|
-
break;
|
|
1110
|
-
case ASTType.Identifier:
|
|
1111
|
-
intoId = intoId || this.nextId();
|
|
1112
|
-
if (nameId) {
|
|
1113
|
-
nameId.context =
|
|
1114
|
-
self.stage === "inputs"
|
|
1115
|
-
? "s"
|
|
1116
|
-
: this.assign(
|
|
1117
|
-
this.nextId(),
|
|
1118
|
-
`${this.getHasOwnProperty("l", ast.name)}?l:s`,
|
|
1119
|
-
);
|
|
1120
|
-
nameId.computed = false;
|
|
1121
|
-
nameId.name = ast.name;
|
|
1122
|
-
}
|
|
1123
|
-
self.if_(
|
|
1124
|
-
self.stage === "inputs" ||
|
|
1125
|
-
self.not(self.getHasOwnProperty("l", ast.name)),
|
|
1126
|
-
() => {
|
|
1127
|
-
self.if_(self.stage === "inputs" || "s", () => {
|
|
1128
|
-
if (create && create !== 1) {
|
|
1129
|
-
self.if_(
|
|
1130
|
-
self.isNull(self.nonComputedMember("s", ast.name)),
|
|
1131
|
-
self.lazyAssign(self.nonComputedMember("s", ast.name), "{}"),
|
|
1132
|
-
);
|
|
1133
|
-
}
|
|
1134
|
-
self.assign(intoId, self.nonComputedMember("s", ast.name));
|
|
1135
|
-
});
|
|
1136
|
-
},
|
|
1137
|
-
intoId &&
|
|
1138
|
-
self.lazyAssign(intoId, self.nonComputedMember("l", ast.name)),
|
|
1139
|
-
);
|
|
1140
|
-
recursionFn(intoId);
|
|
1141
|
-
break;
|
|
1142
|
-
case ASTType.MemberExpression:
|
|
1143
|
-
left = (nameId && (nameId.context = this.nextId())) || this.nextId();
|
|
1144
|
-
intoId = intoId || this.nextId();
|
|
1145
|
-
self.recurse(
|
|
1146
|
-
ast.object,
|
|
1147
|
-
left,
|
|
1148
|
-
undefined,
|
|
1149
|
-
() => {
|
|
1150
|
-
self.if_(
|
|
1151
|
-
self.notNull(left),
|
|
1152
|
-
() => {
|
|
1153
|
-
if (ast.computed) {
|
|
1154
|
-
right = self.nextId();
|
|
1155
|
-
self.recurse(ast.property, right);
|
|
1156
|
-
self.getStringValue(right);
|
|
1157
|
-
if (create && create !== 1) {
|
|
1158
|
-
self.if_(
|
|
1159
|
-
self.not(self.computedMember(left, right)),
|
|
1160
|
-
self.lazyAssign(self.computedMember(left, right), "{}"),
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
expression = self.computedMember(left, right);
|
|
1164
|
-
self.assign(intoId, expression);
|
|
1165
|
-
if (nameId) {
|
|
1166
|
-
nameId.computed = true;
|
|
1167
|
-
nameId.name = right;
|
|
1168
|
-
}
|
|
1169
|
-
} else {
|
|
1170
|
-
if (create && create !== 1) {
|
|
1171
|
-
self.if_(
|
|
1172
|
-
self.isNull(
|
|
1173
|
-
self.nonComputedMember(left, ast.property.name),
|
|
1174
|
-
),
|
|
1175
|
-
self.lazyAssign(
|
|
1176
|
-
self.nonComputedMember(left, ast.property.name),
|
|
1177
|
-
"{}",
|
|
1178
|
-
),
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
expression = self.nonComputedMember(left, ast.property.name);
|
|
1182
|
-
self.assign(intoId, expression);
|
|
1183
|
-
if (nameId) {
|
|
1184
|
-
nameId.computed = false;
|
|
1185
|
-
nameId.name = ast.property.name;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
},
|
|
1189
|
-
() => {
|
|
1190
|
-
self.assign(intoId, "undefined");
|
|
1191
|
-
},
|
|
1192
|
-
);
|
|
1193
|
-
recursionFn(intoId);
|
|
1194
|
-
},
|
|
1195
|
-
!!create,
|
|
1196
|
-
);
|
|
1197
|
-
break;
|
|
1198
|
-
case ASTType.CallExpression:
|
|
1199
|
-
intoId = intoId || this.nextId();
|
|
1200
|
-
if (ast.filter) {
|
|
1201
|
-
right = self.filter(ast.callee.name);
|
|
1202
|
-
args = [];
|
|
1203
|
-
forEach(ast.arguments, (expr) => {
|
|
1204
|
-
const argument = self.nextId();
|
|
1205
|
-
self.recurse(expr, argument);
|
|
1206
|
-
args.push(argument);
|
|
1207
|
-
});
|
|
1208
|
-
expression = `${right}(${args.join(",")})`;
|
|
1209
|
-
self.assign(intoId, expression);
|
|
1210
|
-
recursionFn(intoId);
|
|
1211
|
-
} else {
|
|
1212
|
-
right = self.nextId();
|
|
1213
|
-
left = {};
|
|
1214
|
-
args = [];
|
|
1215
|
-
self.recurse(ast.callee, right, left, () => {
|
|
1216
|
-
self.if_(
|
|
1217
|
-
self.notNull(right),
|
|
1218
|
-
() => {
|
|
1219
|
-
forEach(ast.arguments, (expr) => {
|
|
1220
|
-
self.recurse(
|
|
1221
|
-
expr,
|
|
1222
|
-
ast.constant ? undefined : self.nextId(),
|
|
1223
|
-
undefined,
|
|
1224
|
-
(argument) => {
|
|
1225
|
-
args.push(argument);
|
|
1226
|
-
},
|
|
1227
|
-
);
|
|
1228
|
-
});
|
|
1229
|
-
if (left.name) {
|
|
1230
|
-
expression = `${self.member(
|
|
1231
|
-
left.context,
|
|
1232
|
-
left.name,
|
|
1233
|
-
left.computed,
|
|
1234
|
-
)}(${args.join(",")})`;
|
|
1235
|
-
} else {
|
|
1236
|
-
expression = `${right}(${args.join(",")})`;
|
|
1237
|
-
}
|
|
1238
|
-
self.assign(intoId, expression);
|
|
1239
|
-
},
|
|
1240
|
-
() => {
|
|
1241
|
-
self.assign(intoId, "undefined");
|
|
1242
|
-
},
|
|
1243
|
-
);
|
|
1244
|
-
recursionFn(intoId);
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
break;
|
|
1248
|
-
case ASTType.AssignmentExpression:
|
|
1249
|
-
right = this.nextId();
|
|
1250
|
-
left = {};
|
|
1251
|
-
this.recurse(
|
|
1252
|
-
ast.left,
|
|
1253
|
-
undefined,
|
|
1254
|
-
left,
|
|
1255
|
-
() => {
|
|
1256
|
-
self.if_(self.notNull(left.context), () => {
|
|
1257
|
-
self.recurse(ast.right, right);
|
|
1258
|
-
expression =
|
|
1259
|
-
self.member(left.context, left.name, left.computed) +
|
|
1260
|
-
ast.operator +
|
|
1261
|
-
right;
|
|
1262
|
-
self.assign(intoId, expression);
|
|
1263
|
-
recursionFn(intoId || expression);
|
|
1264
|
-
});
|
|
1265
|
-
},
|
|
1266
|
-
1,
|
|
1267
|
-
);
|
|
1268
|
-
break;
|
|
1269
|
-
case ASTType.ArrayExpression:
|
|
1270
|
-
args = [];
|
|
1271
|
-
forEach(ast.elements, (expr) => {
|
|
1272
|
-
self.recurse(
|
|
1273
|
-
expr,
|
|
1274
|
-
ast.constant ? undefined : self.nextId(),
|
|
1275
|
-
undefined,
|
|
1276
|
-
(argument) => {
|
|
1277
|
-
args.push(argument);
|
|
1278
|
-
},
|
|
1279
|
-
);
|
|
1280
|
-
});
|
|
1281
|
-
expression = `[${args.join(",")}]`;
|
|
1282
|
-
this.assign(intoId, expression);
|
|
1283
|
-
recursionFn(intoId || expression);
|
|
1284
|
-
break;
|
|
1285
|
-
case ASTType.ObjectExpression:
|
|
1286
|
-
args = [];
|
|
1287
|
-
computed = false;
|
|
1288
|
-
forEach(ast.properties, (property) => {
|
|
1289
|
-
if (property.computed) {
|
|
1290
|
-
computed = true;
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
if (computed) {
|
|
1294
|
-
intoId = intoId || this.nextId();
|
|
1295
|
-
this.assign(intoId, "{}");
|
|
1296
|
-
forEach(ast.properties, (property) => {
|
|
1297
|
-
if (property.computed) {
|
|
1298
|
-
left = self.nextId();
|
|
1299
|
-
self.recurse(property.key, left);
|
|
1300
|
-
} else {
|
|
1301
|
-
left =
|
|
1302
|
-
property.key.type === ASTType.Identifier
|
|
1303
|
-
? property.key.name
|
|
1304
|
-
: `${property.key.value}`;
|
|
1305
|
-
}
|
|
1306
|
-
right = self.nextId();
|
|
1307
|
-
self.recurse(property.value, right);
|
|
1308
|
-
self.assign(self.member(intoId, left, property.computed), right);
|
|
1309
|
-
});
|
|
1310
|
-
} else {
|
|
1311
|
-
forEach(ast.properties, (property) => {
|
|
1312
|
-
self.recurse(
|
|
1313
|
-
property.value,
|
|
1314
|
-
ast.constant ? undefined : self.nextId(),
|
|
1315
|
-
undefined,
|
|
1316
|
-
(expr) => {
|
|
1317
|
-
args.push(
|
|
1318
|
-
`${self.escape(
|
|
1319
|
-
property.key.type === ASTType.Identifier
|
|
1320
|
-
? property.key.name
|
|
1321
|
-
: `${property.key.value}`,
|
|
1322
|
-
)}:${expr}`,
|
|
1323
|
-
);
|
|
1324
|
-
},
|
|
1325
|
-
);
|
|
1326
|
-
});
|
|
1327
|
-
expression = `{${args.join(",")}}`;
|
|
1328
|
-
this.assign(intoId, expression);
|
|
1329
|
-
}
|
|
1330
|
-
recursionFn(intoId || expression);
|
|
1331
|
-
break;
|
|
1332
|
-
case ASTType.ThisExpression:
|
|
1333
|
-
this.assign(intoId, "s");
|
|
1334
|
-
recursionFn(intoId || "s");
|
|
1335
|
-
break;
|
|
1336
|
-
case ASTType.LocalsExpression:
|
|
1337
|
-
this.assign(intoId, "l");
|
|
1338
|
-
recursionFn(intoId || "l");
|
|
1339
|
-
break;
|
|
1340
|
-
case ASTType.NGValueParameter:
|
|
1341
|
-
this.assign(intoId, "v");
|
|
1342
|
-
recursionFn(intoId || "v");
|
|
1343
|
-
break;
|
|
1344
|
-
}
|
|
1345
|
-
},
|
|
1346
|
-
|
|
1347
|
-
getHasOwnProperty(element, property) {
|
|
1348
|
-
const key = `${element}.${property}`;
|
|
1349
|
-
const { own } = this.current();
|
|
1350
|
-
if (!Object.prototype.hasOwnProperty.call(own, key)) {
|
|
1351
|
-
own[key] = this.nextId(
|
|
1352
|
-
false,
|
|
1353
|
-
`${element}&&(${this.escape(property)} in ${element})`,
|
|
1354
|
-
);
|
|
1355
|
-
}
|
|
1356
|
-
return own[key];
|
|
1357
|
-
},
|
|
1358
|
-
|
|
1359
|
-
assign(id, value) {
|
|
1360
|
-
if (!id) return;
|
|
1361
|
-
this.current().body.push(id, "=", value, ";");
|
|
1362
|
-
return id;
|
|
1363
|
-
},
|
|
1364
|
-
|
|
1365
|
-
filter(filterName) {
|
|
1366
|
-
if (!Object.prototype.hasOwnProperty.call(this.state.filters, filterName)) {
|
|
1367
|
-
this.state.filters[filterName] = this.nextId(true);
|
|
1368
|
-
}
|
|
1369
|
-
return this.state.filters[filterName];
|
|
1370
|
-
},
|
|
1371
|
-
|
|
1372
|
-
ifDefined(id, defaultValue) {
|
|
1373
|
-
return `ifDefined(${id},${this.escape(defaultValue)})`;
|
|
1374
|
-
},
|
|
1375
|
-
|
|
1376
|
-
plus(left, right) {
|
|
1377
|
-
return `plus(${left},${right})`;
|
|
1378
|
-
},
|
|
1379
|
-
|
|
1380
|
-
return_(id) {
|
|
1381
|
-
this.current().body.push("return ", id, ";");
|
|
1382
|
-
},
|
|
1383
|
-
|
|
1384
|
-
if_(test, alternate, consequent) {
|
|
1385
|
-
if (test === true) {
|
|
1386
|
-
alternate();
|
|
1387
|
-
} else {
|
|
1388
|
-
const { body } = this.current();
|
|
1389
|
-
body.push("if(", test, "){");
|
|
1390
|
-
alternate();
|
|
1391
|
-
body.push("}");
|
|
1392
|
-
if (consequent) {
|
|
1393
|
-
body.push("else{");
|
|
1394
|
-
consequent();
|
|
1395
|
-
body.push("}");
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
},
|
|
1399
|
-
|
|
1400
|
-
not(expression) {
|
|
1401
|
-
return `!(${expression})`;
|
|
1402
|
-
},
|
|
1403
|
-
|
|
1404
|
-
isNull(expression) {
|
|
1405
|
-
return `${expression}==null`;
|
|
1406
|
-
},
|
|
1407
|
-
|
|
1408
|
-
notNull(expression) {
|
|
1409
|
-
return `${expression}!=null`;
|
|
1410
|
-
},
|
|
1411
|
-
|
|
1412
|
-
nonComputedMember(left, right) {
|
|
1413
|
-
const SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
|
|
1414
|
-
const UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
|
|
1415
|
-
if (SAFE_IDENTIFIER.test(right)) {
|
|
1416
|
-
return `${left}.${right}`;
|
|
1417
|
-
}
|
|
1418
|
-
return `${left}["${right.replace(
|
|
1419
|
-
UNSAFE_CHARACTERS,
|
|
1420
|
-
this.stringEscapeFn,
|
|
1421
|
-
)}"]`;
|
|
1422
|
-
},
|
|
1423
|
-
|
|
1424
|
-
computedMember(left, right) {
|
|
1425
|
-
return `${left}[${right}]`;
|
|
1426
|
-
},
|
|
1427
|
-
|
|
1428
|
-
member(left, right, computed) {
|
|
1429
|
-
if (computed) return this.computedMember(left, right);
|
|
1430
|
-
return this.nonComputedMember(left, right);
|
|
1431
|
-
},
|
|
1432
|
-
|
|
1433
|
-
getStringValue(item) {
|
|
1434
|
-
this.assign(item, `getStringValue(${item})`);
|
|
1435
|
-
},
|
|
1436
|
-
|
|
1437
|
-
lazyRecurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
|
|
1438
|
-
const self = this;
|
|
1439
|
-
return function () {
|
|
1440
|
-
self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
|
|
1441
|
-
};
|
|
1442
|
-
},
|
|
1443
|
-
|
|
1444
|
-
lazyAssign(id, value) {
|
|
1445
|
-
const self = this;
|
|
1446
|
-
return function () {
|
|
1447
|
-
self.assign(id, value);
|
|
1448
|
-
};
|
|
1449
|
-
},
|
|
1450
|
-
|
|
1451
|
-
stringEscapeRegex: /[^ a-zA-Z0-9]/g,
|
|
1452
|
-
|
|
1453
|
-
stringEscapeFn(c) {
|
|
1454
|
-
return `\\u${`0000${c.charCodeAt(0).toString(16)}`.slice(-4)}`;
|
|
1455
|
-
},
|
|
1456
|
-
|
|
1457
|
-
escape(value) {
|
|
1458
|
-
if (isString(value))
|
|
1459
|
-
return `'${value.replace(this.stringEscapeRegex, this.stringEscapeFn)}'`;
|
|
1460
|
-
if (isNumber(value)) return value.toString();
|
|
1461
|
-
if (value === true) return "true";
|
|
1462
|
-
if (value === false) return "false";
|
|
1463
|
-
if (value === null) return "null";
|
|
1464
|
-
if (typeof value === "undefined") return "undefined";
|
|
1465
|
-
|
|
1466
|
-
throw $parseMinErr("esc", "IMPOSSIBLE");
|
|
1467
|
-
},
|
|
1468
|
-
|
|
1469
|
-
nextId(skip, init) {
|
|
1470
|
-
const id = `v${this.state.nextId++}`;
|
|
1471
|
-
if (!skip) {
|
|
1472
|
-
this.current().vars.push(id + (init ? `=${init}` : ""));
|
|
1473
|
-
}
|
|
1474
|
-
return id;
|
|
1475
|
-
},
|
|
1476
|
-
|
|
1477
|
-
current() {
|
|
1478
|
-
return this.state[this.state.computing];
|
|
1479
|
-
},
|
|
1480
|
-
};
|
|
1481
|
-
|
|
1482
|
-
function ASTInterpreter($filter) {
|
|
1483
|
-
this.$filter = $filter;
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
ASTInterpreter.prototype = {
|
|
1487
|
-
compile(ast) {
|
|
1488
|
-
const self = this;
|
|
1489
|
-
findConstantAndWatchExpressions(ast, self.$filter);
|
|
1490
|
-
let assignable;
|
|
1491
|
-
let assign;
|
|
1492
|
-
if ((assignable = assignableAST(ast))) {
|
|
1493
|
-
assign = this.recurse(assignable);
|
|
1494
|
-
}
|
|
1495
|
-
const toWatch = getInputs(ast.body);
|
|
1496
|
-
let inputs;
|
|
1497
|
-
if (toWatch) {
|
|
1498
|
-
inputs = [];
|
|
1499
|
-
forEach(toWatch, (watch, key) => {
|
|
1500
|
-
const input = self.recurse(watch);
|
|
1501
|
-
input.isPure = watch.isPure;
|
|
1502
|
-
watch.input = input;
|
|
1503
|
-
inputs.push(input);
|
|
1504
|
-
watch.watchId = key;
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
const expressions = [];
|
|
1508
|
-
forEach(ast.body, (expression) => {
|
|
1509
|
-
expressions.push(self.recurse(expression.expression));
|
|
1510
|
-
});
|
|
1511
|
-
const fn =
|
|
1512
|
-
ast.body.length === 0
|
|
1513
|
-
? () => {}
|
|
1514
|
-
: ast.body.length === 1
|
|
1515
|
-
? expressions[0]
|
|
1516
|
-
: function (scope, locals) {
|
|
1517
|
-
let lastValue;
|
|
1518
|
-
forEach(expressions, (exp) => {
|
|
1519
|
-
lastValue = exp(scope, locals);
|
|
1520
|
-
});
|
|
1521
|
-
return lastValue;
|
|
1522
|
-
};
|
|
1523
|
-
if (assign) {
|
|
1524
|
-
fn.assign = function (scope, value, locals) {
|
|
1525
|
-
return assign(scope, locals, value);
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
if (inputs) {
|
|
1529
|
-
fn.inputs = inputs;
|
|
1530
|
-
}
|
|
1531
|
-
return fn;
|
|
1532
|
-
},
|
|
1533
|
-
|
|
1534
|
-
recurse(ast, context, create) {
|
|
1535
|
-
let left;
|
|
1536
|
-
let right;
|
|
1537
|
-
const self = this;
|
|
1538
|
-
let args;
|
|
1539
|
-
if (ast.input) {
|
|
1540
|
-
return this.inputs(ast.input, ast.watchId);
|
|
1541
|
-
}
|
|
1542
|
-
switch (ast.type) {
|
|
1543
|
-
case ASTType.Literal:
|
|
1544
|
-
return this.value(ast.value, context);
|
|
1545
|
-
case ASTType.UnaryExpression:
|
|
1546
|
-
right = this.recurse(ast.argument);
|
|
1547
|
-
return this[`unary${ast.operator}`](right, context);
|
|
1548
|
-
case ASTType.BinaryExpression:
|
|
1549
|
-
left = this.recurse(ast.left);
|
|
1550
|
-
right = this.recurse(ast.right);
|
|
1551
|
-
return this[`binary${ast.operator}`](left, right, context);
|
|
1552
|
-
case ASTType.LogicalExpression:
|
|
1553
|
-
left = this.recurse(ast.left);
|
|
1554
|
-
right = this.recurse(ast.right);
|
|
1555
|
-
return this[`binary${ast.operator}`](left, right, context);
|
|
1556
|
-
case ASTType.ConditionalExpression:
|
|
1557
|
-
return this["ternary?:"](
|
|
1558
|
-
this.recurse(ast.test),
|
|
1559
|
-
this.recurse(ast.alternate),
|
|
1560
|
-
this.recurse(ast.consequent),
|
|
1561
|
-
context,
|
|
1562
|
-
);
|
|
1563
|
-
case ASTType.Identifier:
|
|
1564
|
-
return self.identifier(ast.name, context, create);
|
|
1565
|
-
case ASTType.MemberExpression:
|
|
1566
|
-
left = this.recurse(ast.object, false, !!create);
|
|
1567
|
-
if (!ast.computed) {
|
|
1568
|
-
right = ast.property.name;
|
|
1569
|
-
}
|
|
1570
|
-
if (ast.computed) right = this.recurse(ast.property);
|
|
1571
|
-
return ast.computed
|
|
1572
|
-
? this.computedMember(left, right, context, create)
|
|
1573
|
-
: this.nonComputedMember(left, right, context, create);
|
|
1574
|
-
case ASTType.CallExpression:
|
|
1575
|
-
args = [];
|
|
1576
|
-
forEach(ast.arguments, (expr) => {
|
|
1577
|
-
args.push(self.recurse(expr));
|
|
1578
|
-
});
|
|
1579
|
-
if (ast.filter) right = this.$filter(ast.callee.name);
|
|
1580
|
-
if (!ast.filter) right = this.recurse(ast.callee, true);
|
|
1581
|
-
return ast.filter
|
|
1582
|
-
? function (scope, locals, assign, inputs) {
|
|
1583
|
-
const values = [];
|
|
1584
|
-
for (let i = 0; i < args.length; ++i) {
|
|
1585
|
-
values.push(args[i](scope, locals, assign, inputs));
|
|
1586
|
-
}
|
|
1587
|
-
const value = right.apply(undefined, values, inputs);
|
|
1588
|
-
return context
|
|
1589
|
-
? { context: undefined, name: undefined, value }
|
|
1590
|
-
: value;
|
|
1591
|
-
}
|
|
1592
|
-
: function (scope, locals, assign, inputs) {
|
|
1593
|
-
const rhs = right(scope, locals, assign, inputs);
|
|
1594
|
-
let value;
|
|
1595
|
-
if (rhs.value != null) {
|
|
1596
|
-
const values = [];
|
|
1597
|
-
for (let i = 0; i < args.length; ++i) {
|
|
1598
|
-
values.push(args[i](scope, locals, assign, inputs));
|
|
1599
|
-
}
|
|
1600
|
-
value = rhs.value.apply(rhs.context, values);
|
|
1601
|
-
}
|
|
1602
|
-
return context ? { value } : value;
|
|
1603
|
-
};
|
|
1604
|
-
case ASTType.AssignmentExpression:
|
|
1605
|
-
left = this.recurse(ast.left, true, 1);
|
|
1606
|
-
right = this.recurse(ast.right);
|
|
1607
|
-
return function (scope, locals, assign, inputs) {
|
|
1608
|
-
const lhs = left(scope, locals, assign, inputs);
|
|
1609
|
-
const rhs = right(scope, locals, assign, inputs);
|
|
1610
|
-
lhs.context[lhs.name] = rhs;
|
|
1611
|
-
return context ? { value: rhs } : rhs;
|
|
1612
|
-
};
|
|
1613
|
-
case ASTType.ArrayExpression:
|
|
1614
|
-
args = [];
|
|
1615
|
-
forEach(ast.elements, (expr) => {
|
|
1616
|
-
args.push(self.recurse(expr));
|
|
1617
|
-
});
|
|
1618
|
-
return function (scope, locals, assign, inputs) {
|
|
1619
|
-
const value = [];
|
|
1620
|
-
for (let i = 0; i < args.length; ++i) {
|
|
1621
|
-
value.push(args[i](scope, locals, assign, inputs));
|
|
1622
|
-
}
|
|
1623
|
-
return context ? { value } : value;
|
|
1624
|
-
};
|
|
1625
|
-
case ASTType.ObjectExpression:
|
|
1626
|
-
args = [];
|
|
1627
|
-
forEach(ast.properties, (property) => {
|
|
1628
|
-
if (property.computed) {
|
|
1629
|
-
args.push({
|
|
1630
|
-
key: self.recurse(property.key),
|
|
1631
|
-
computed: true,
|
|
1632
|
-
value: self.recurse(property.value),
|
|
1633
|
-
});
|
|
1634
|
-
} else {
|
|
1635
|
-
args.push({
|
|
1636
|
-
key:
|
|
1637
|
-
property.key.type === ASTType.Identifier
|
|
1638
|
-
? property.key.name
|
|
1639
|
-
: `${property.key.value}`,
|
|
1640
|
-
computed: false,
|
|
1641
|
-
value: self.recurse(property.value),
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
|
-
});
|
|
1645
|
-
return function (scope, locals, assign, inputs) {
|
|
1646
|
-
const value = {};
|
|
1647
|
-
for (let i = 0; i < args.length; ++i) {
|
|
1648
|
-
if (args[i].computed) {
|
|
1649
|
-
value[args[i].key(scope, locals, assign, inputs)] = args[i].value(
|
|
1650
|
-
scope,
|
|
1651
|
-
locals,
|
|
1652
|
-
assign,
|
|
1653
|
-
inputs,
|
|
1654
|
-
);
|
|
1655
|
-
} else {
|
|
1656
|
-
value[args[i].key] = args[i].value(scope, locals, assign, inputs);
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
return context ? { value } : value;
|
|
1660
|
-
};
|
|
1661
|
-
case ASTType.ThisExpression:
|
|
1662
|
-
return function (scope) {
|
|
1663
|
-
return context ? { value: scope } : scope;
|
|
1664
|
-
};
|
|
1665
|
-
case ASTType.LocalsExpression:
|
|
1666
|
-
return function (scope, locals) {
|
|
1667
|
-
return context ? { value: locals } : locals;
|
|
1668
|
-
};
|
|
1669
|
-
case ASTType.NGValueParameter:
|
|
1670
|
-
return function (scope, locals, assign) {
|
|
1671
|
-
return context ? { value: assign } : assign;
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
},
|
|
1675
|
-
|
|
1676
|
-
"unary+": function (argument, context) {
|
|
1677
|
-
return function (scope, locals, assign, inputs) {
|
|
1678
|
-
let arg = argument(scope, locals, assign, inputs);
|
|
1679
|
-
if (isDefined(arg)) {
|
|
1680
|
-
arg = +arg;
|
|
1681
|
-
} else {
|
|
1682
|
-
arg = 0;
|
|
1683
|
-
}
|
|
1684
|
-
return context ? { value: arg } : arg;
|
|
1685
|
-
};
|
|
1686
|
-
},
|
|
1687
|
-
"unary-": function (argument, context) {
|
|
1688
|
-
return function (scope, locals, assign, inputs) {
|
|
1689
|
-
let arg = argument(scope, locals, assign, inputs);
|
|
1690
|
-
if (isDefined(arg)) {
|
|
1691
|
-
arg = -arg;
|
|
1692
|
-
} else {
|
|
1693
|
-
arg = -0;
|
|
1694
|
-
}
|
|
1695
|
-
return context ? { value: arg } : arg;
|
|
1696
|
-
};
|
|
1697
|
-
},
|
|
1698
|
-
"unary!": function (argument, context) {
|
|
1699
|
-
return function (scope, locals, assign, inputs) {
|
|
1700
|
-
const arg = !argument(scope, locals, assign, inputs);
|
|
1701
|
-
return context ? { value: arg } : arg;
|
|
1702
|
-
};
|
|
1703
|
-
},
|
|
1704
|
-
"binary+": function (left, right, context) {
|
|
1705
|
-
return function (scope, locals, assign, inputs) {
|
|
1706
|
-
const lhs = left(scope, locals, assign, inputs);
|
|
1707
|
-
const rhs = right(scope, locals, assign, inputs);
|
|
1708
|
-
const arg = plusFn(lhs, rhs);
|
|
1709
|
-
return context ? { value: arg } : arg;
|
|
1710
|
-
};
|
|
1711
|
-
},
|
|
1712
|
-
"binary-": function (left, right, context) {
|
|
1713
|
-
return function (scope, locals, assign, inputs) {
|
|
1714
|
-
const lhs = left(scope, locals, assign, inputs);
|
|
1715
|
-
const rhs = right(scope, locals, assign, inputs);
|
|
1716
|
-
const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
|
|
1717
|
-
return context ? { value: arg } : arg;
|
|
1718
|
-
};
|
|
1719
|
-
},
|
|
1720
|
-
"binary*": function (left, right, context) {
|
|
1721
|
-
return function (scope, locals, assign, inputs) {
|
|
1722
|
-
const arg =
|
|
1723
|
-
left(scope, locals, assign, inputs) *
|
|
1724
|
-
right(scope, locals, assign, inputs);
|
|
1725
|
-
return context ? { value: arg } : arg;
|
|
1726
|
-
};
|
|
1727
|
-
},
|
|
1728
|
-
"binary/": function (left, right, context) {
|
|
1729
|
-
return function (scope, locals, assign, inputs) {
|
|
1730
|
-
const arg =
|
|
1731
|
-
left(scope, locals, assign, inputs) /
|
|
1732
|
-
right(scope, locals, assign, inputs);
|
|
1733
|
-
return context ? { value: arg } : arg;
|
|
1734
|
-
};
|
|
1735
|
-
},
|
|
1736
|
-
"binary%": function (left, right, context) {
|
|
1737
|
-
return function (scope, locals, assign, inputs) {
|
|
1738
|
-
const arg =
|
|
1739
|
-
left(scope, locals, assign, inputs) %
|
|
1740
|
-
right(scope, locals, assign, inputs);
|
|
1741
|
-
return context ? { value: arg } : arg;
|
|
1742
|
-
};
|
|
1743
|
-
},
|
|
1744
|
-
"binary===": function (left, right, context) {
|
|
1745
|
-
return function (scope, locals, assign, inputs) {
|
|
1746
|
-
const arg =
|
|
1747
|
-
left(scope, locals, assign, inputs) ===
|
|
1748
|
-
right(scope, locals, assign, inputs);
|
|
1749
|
-
return context ? { value: arg } : arg;
|
|
1750
|
-
};
|
|
1751
|
-
},
|
|
1752
|
-
"binary!==": function (left, right, context) {
|
|
1753
|
-
return function (scope, locals, assign, inputs) {
|
|
1754
|
-
const arg =
|
|
1755
|
-
left(scope, locals, assign, inputs) !==
|
|
1756
|
-
right(scope, locals, assign, inputs);
|
|
1757
|
-
return context ? { value: arg } : arg;
|
|
1758
|
-
};
|
|
1759
|
-
},
|
|
1760
|
-
"binary==": function (left, right, context) {
|
|
1761
|
-
return function (scope, locals, assign, inputs) {
|
|
1762
|
-
const arg =
|
|
1763
|
-
left(scope, locals, assign, inputs) ==
|
|
1764
|
-
right(scope, locals, assign, inputs);
|
|
1765
|
-
return context ? { value: arg } : arg;
|
|
1766
|
-
};
|
|
1767
|
-
},
|
|
1768
|
-
"binary!=": function (left, right, context) {
|
|
1769
|
-
return function (scope, locals, assign, inputs) {
|
|
1770
|
-
const arg =
|
|
1771
|
-
left(scope, locals, assign, inputs) !=
|
|
1772
|
-
right(scope, locals, assign, inputs);
|
|
1773
|
-
return context ? { value: arg } : arg;
|
|
1774
|
-
};
|
|
1775
|
-
},
|
|
1776
|
-
"binary<": function (left, right, context) {
|
|
1777
|
-
return function (scope, locals, assign, inputs) {
|
|
1778
|
-
const arg =
|
|
1779
|
-
left(scope, locals, assign, inputs) <
|
|
1780
|
-
right(scope, locals, assign, inputs);
|
|
1781
|
-
return context ? { value: arg } : arg;
|
|
1782
|
-
};
|
|
1783
|
-
},
|
|
1784
|
-
"binary>": function (left, right, context) {
|
|
1785
|
-
return function (scope, locals, assign, inputs) {
|
|
1786
|
-
const arg =
|
|
1787
|
-
left(scope, locals, assign, inputs) >
|
|
1788
|
-
right(scope, locals, assign, inputs);
|
|
1789
|
-
return context ? { value: arg } : arg;
|
|
1790
|
-
};
|
|
1791
|
-
},
|
|
1792
|
-
"binary<=": function (left, right, context) {
|
|
1793
|
-
return function (scope, locals, assign, inputs) {
|
|
1794
|
-
const arg =
|
|
1795
|
-
left(scope, locals, assign, inputs) <=
|
|
1796
|
-
right(scope, locals, assign, inputs);
|
|
1797
|
-
return context ? { value: arg } : arg;
|
|
1798
|
-
};
|
|
1799
|
-
},
|
|
1800
|
-
"binary>=": function (left, right, context) {
|
|
1801
|
-
return function (scope, locals, assign, inputs) {
|
|
1802
|
-
const arg =
|
|
1803
|
-
left(scope, locals, assign, inputs) >=
|
|
1804
|
-
right(scope, locals, assign, inputs);
|
|
1805
|
-
return context ? { value: arg } : arg;
|
|
1806
|
-
};
|
|
1807
|
-
},
|
|
1808
|
-
"binary&&": function (left, right, context) {
|
|
1809
|
-
return function (scope, locals, assign, inputs) {
|
|
1810
|
-
const arg =
|
|
1811
|
-
left(scope, locals, assign, inputs) &&
|
|
1812
|
-
right(scope, locals, assign, inputs);
|
|
1813
|
-
return context ? { value: arg } : arg;
|
|
1814
|
-
};
|
|
1815
|
-
},
|
|
1816
|
-
"binary||": function (left, right, context) {
|
|
1817
|
-
return function (scope, locals, assign, inputs) {
|
|
1818
|
-
const arg =
|
|
1819
|
-
left(scope, locals, assign, inputs) ||
|
|
1820
|
-
right(scope, locals, assign, inputs);
|
|
1821
|
-
return context ? { value: arg } : arg;
|
|
1822
|
-
};
|
|
1823
|
-
},
|
|
1824
|
-
"ternary?:": function (test, alternate, consequent, context) {
|
|
1825
|
-
return function (scope, locals, assign, inputs) {
|
|
1826
|
-
const arg = test(scope, locals, assign, inputs)
|
|
1827
|
-
? alternate(scope, locals, assign, inputs)
|
|
1828
|
-
: consequent(scope, locals, assign, inputs);
|
|
1829
|
-
return context ? { value: arg } : arg;
|
|
1830
|
-
};
|
|
1831
|
-
},
|
|
1832
|
-
value(value, context) {
|
|
1833
|
-
return function () {
|
|
1834
|
-
return context ? { context: undefined, name: undefined, value } : value;
|
|
1835
|
-
};
|
|
1836
|
-
},
|
|
1837
|
-
identifier(name, context, create) {
|
|
1838
|
-
return function (scope, locals) {
|
|
1839
|
-
const base = locals && name in locals ? locals : scope;
|
|
1840
|
-
if (create && create !== 1 && base && base[name] == null) {
|
|
1841
|
-
base[name] = {};
|
|
1842
|
-
}
|
|
1843
|
-
const value = base ? base[name] : undefined;
|
|
1844
|
-
if (context) {
|
|
1845
|
-
return { context: base, name, value };
|
|
1846
|
-
}
|
|
1847
|
-
return value;
|
|
1848
|
-
};
|
|
1849
|
-
},
|
|
1850
|
-
computedMember(left, right, context, create) {
|
|
1851
|
-
return function (scope, locals, assign, inputs) {
|
|
1852
|
-
const lhs = left(scope, locals, assign, inputs);
|
|
1853
|
-
let rhs;
|
|
1854
|
-
let value;
|
|
1855
|
-
if (lhs != null) {
|
|
1856
|
-
rhs = right(scope, locals, assign, inputs);
|
|
1857
|
-
rhs = getStringValue(rhs);
|
|
1858
|
-
if (create && create !== 1) {
|
|
1859
|
-
if (lhs && !lhs[rhs]) {
|
|
1860
|
-
lhs[rhs] = {};
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
value = lhs[rhs];
|
|
1864
|
-
}
|
|
1865
|
-
if (context) {
|
|
1866
|
-
return { context: lhs, name: rhs, value };
|
|
1867
|
-
}
|
|
1868
|
-
return value;
|
|
1869
|
-
};
|
|
1870
|
-
},
|
|
1871
|
-
nonComputedMember(left, right, context, create) {
|
|
1872
|
-
return function (scope, locals, assign, inputs) {
|
|
1873
|
-
const lhs = left(scope, locals, assign, inputs);
|
|
1874
|
-
if (create && create !== 1) {
|
|
1875
|
-
if (lhs && lhs[right] == null) {
|
|
1876
|
-
lhs[right] = {};
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
const value = lhs != null ? lhs[right] : undefined;
|
|
1880
|
-
if (context) {
|
|
1881
|
-
return { context: lhs, name: right, value };
|
|
1882
|
-
}
|
|
1883
|
-
return value;
|
|
1884
|
-
};
|
|
1885
|
-
},
|
|
1886
|
-
inputs(input, watchId) {
|
|
1887
|
-
return function (scope, value, locals, inputs) {
|
|
1888
|
-
if (inputs) return inputs[watchId];
|
|
1889
|
-
return input(scope, value, locals);
|
|
1890
|
-
};
|
|
1891
|
-
},
|
|
1892
|
-
};
|
|
1893
|
-
|
|
1894
|
-
/**
|
|
1895
|
-
* @constructor
|
|
1896
|
-
*/
|
|
1897
|
-
class Parser {
|
|
1898
|
-
constructor(lexer, $filter, options) {
|
|
1899
|
-
this.ast = new AST(lexer, options);
|
|
1900
|
-
this.astCompiler = options.csp
|
|
1901
|
-
? new ASTInterpreter($filter)
|
|
1902
|
-
: new ASTCompiler($filter);
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
parse(text) {
|
|
1906
|
-
const { ast, oneTime } = this.getAst(text);
|
|
1907
|
-
const fn = this.astCompiler.compile(ast);
|
|
1908
|
-
fn.literal = isLiteral(ast);
|
|
1909
|
-
fn.constant = isConstant(ast);
|
|
1910
|
-
fn.oneTime = oneTime;
|
|
1911
|
-
return fn;
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
getAst(exp) {
|
|
1915
|
-
let oneTime = false;
|
|
1916
|
-
exp = exp.trim();
|
|
1917
|
-
|
|
1918
|
-
if (exp.startsWith("::")) {
|
|
1919
|
-
oneTime = true;
|
|
1920
|
-
exp = exp.substring(2);
|
|
1921
|
-
}
|
|
1922
|
-
return {
|
|
1923
|
-
ast: this.ast.ast(exp),
|
|
1924
|
-
oneTime,
|
|
1925
|
-
};
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
function getValueOf(value) {
|
|
1930
|
-
return isFunction(value.valueOf)
|
|
1931
|
-
? value.valueOf()
|
|
1932
|
-
: objectValueOf.call(value);
|
|
1933
|
-
}
|
|
12
|
+
export const $parseMinErr = minErr("$parse");
|
|
1934
13
|
|
|
1935
14
|
/// ////////////////////////////////
|
|
1936
15
|
|
|
1937
16
|
/**
|
|
1938
|
-
* @
|
|
1939
|
-
* @name $parse
|
|
1940
|
-
* @kind function
|
|
1941
|
-
*
|
|
1942
|
-
* @description
|
|
1943
|
-
*
|
|
1944
|
-
* Converts AngularJS {@link guide/expression expression} into a function.
|
|
1945
|
-
*
|
|
1946
|
-
* ```js
|
|
1947
|
-
* let getter = $parse('user.name');
|
|
1948
|
-
* let setter = getter.assign;
|
|
1949
|
-
* let context = {user:{name:'AngularJS'}};
|
|
1950
|
-
* let locals = {user:{name:'local'}};
|
|
1951
|
-
*
|
|
1952
|
-
* expect(getter(context)).toEqual('AngularJS');
|
|
1953
|
-
* setter(context, 'newValue');
|
|
1954
|
-
* expect(context.user.name).toEqual('newValue');
|
|
1955
|
-
* expect(getter(context, locals)).toEqual('local');
|
|
1956
|
-
* ```
|
|
1957
|
-
*
|
|
1958
|
-
*
|
|
1959
|
-
* @param {string} expression String expression to compile.
|
|
1960
|
-
* @returns {function(context, locals)} a function which represents the compiled expression:
|
|
1961
|
-
*
|
|
1962
|
-
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
|
1963
|
-
* are evaluated against (typically a scope object).
|
|
1964
|
-
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
|
1965
|
-
* `context`.
|
|
1966
|
-
*
|
|
1967
|
-
* The returned function also has the following properties:
|
|
1968
|
-
* * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
|
|
1969
|
-
* literal.
|
|
1970
|
-
* * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
|
|
1971
|
-
* constant literals.
|
|
1972
|
-
* * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
|
|
1973
|
-
* set to a function to change its value on the given context.
|
|
1974
|
-
*
|
|
17
|
+
* @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): import('../../types').CompiledExpression} ParseService
|
|
1975
18
|
*/
|
|
1976
19
|
|
|
1977
20
|
export const literals = {
|
|
@@ -1981,15 +24,6 @@ export const literals = {
|
|
|
1981
24
|
undefined,
|
|
1982
25
|
};
|
|
1983
26
|
|
|
1984
|
-
/**
|
|
1985
|
-
* @ngdoc provider
|
|
1986
|
-
* @name $parseProvider
|
|
1987
|
-
*
|
|
1988
|
-
*
|
|
1989
|
-
* @description
|
|
1990
|
-
* `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
|
|
1991
|
-
* service.
|
|
1992
|
-
*/
|
|
1993
27
|
export function $ParseProvider() {
|
|
1994
28
|
const cache = Object.create(null);
|
|
1995
29
|
const literals = {
|
|
@@ -1998,7 +32,11 @@ export function $ParseProvider() {
|
|
|
1998
32
|
null: null,
|
|
1999
33
|
undefined: undefined,
|
|
2000
34
|
};
|
|
2001
|
-
|
|
35
|
+
/** @type {function(any):boolean?} */
|
|
36
|
+
var identStart;
|
|
37
|
+
|
|
38
|
+
/** @type {function(any):boolean?} */
|
|
39
|
+
var identContinue;
|
|
2002
40
|
|
|
2003
41
|
/**
|
|
2004
42
|
* @ngdoc method
|
|
@@ -2034,9 +72,9 @@ export function $ParseProvider() {
|
|
|
2034
72
|
* Since this function will be called extensively, keep the implementation of these functions fast,
|
|
2035
73
|
* as the performance of these functions have a direct impact on the expressions parsing speed.
|
|
2036
74
|
*
|
|
2037
|
-
* @param {function=} identifierStart The function that will decide whether the given character is
|
|
75
|
+
* @param {function(any):boolean=} identifierStart The function that will decide whether the given character is
|
|
2038
76
|
* a valid identifier start character.
|
|
2039
|
-
* @param {function=} identifierContinue The function that will decide whether the given character is
|
|
77
|
+
* @param {function(any):boolean=} identifierContinue The function that will decide whether the given character is
|
|
2040
78
|
* a valid identifier continue character.
|
|
2041
79
|
*/
|
|
2042
80
|
this.setIdentifierFns = function (identifierStart, identifierContinue) {
|
|
@@ -2069,7 +107,10 @@ export function $ParseProvider() {
|
|
|
2069
107
|
parsedExpression = cache[cacheKey];
|
|
2070
108
|
|
|
2071
109
|
if (!parsedExpression) {
|
|
2072
|
-
var lexer = new Lexer(
|
|
110
|
+
var lexer = new Lexer({
|
|
111
|
+
isIdentifierContinue: $parseOptions.isIdentifierContinue,
|
|
112
|
+
isIdentifierStart: $parseOptions.isIdentifierStart,
|
|
113
|
+
});
|
|
2073
114
|
var parser = new Parser(lexer, $filter, $parseOptions);
|
|
2074
115
|
parsedExpression = parser.parse(exp);
|
|
2075
116
|
|
|
@@ -2116,7 +157,7 @@ export function $ParseProvider() {
|
|
|
2116
157
|
}
|
|
2117
158
|
|
|
2118
159
|
//Primitive or NaN
|
|
2119
|
-
|
|
160
|
+
|
|
2120
161
|
return (
|
|
2121
162
|
newValue === oldValueOfValue ||
|
|
2122
163
|
(newValue !== newValue && oldValueOfValue !== oldValueOfValue)
|
|
@@ -2393,7 +434,7 @@ export function inputsWatchDelegate(
|
|
|
2393
434
|
|
|
2394
435
|
if (inputExpressions.length === 1) {
|
|
2395
436
|
let oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
|
|
2396
|
-
|
|
437
|
+
|
|
2397
438
|
inputExpressions = inputExpressions[0];
|
|
2398
439
|
return scope.$watch(
|
|
2399
440
|
($scope) => {
|
|
@@ -2428,12 +469,10 @@ export function inputsWatchDelegate(
|
|
|
2428
469
|
(scope) => {
|
|
2429
470
|
let changed = false;
|
|
2430
471
|
|
|
2431
|
-
// eslint-disable-next-line no-plusplus
|
|
2432
472
|
for (let i = 0, ii = inputExpressions.length; i < ii; i++) {
|
|
2433
473
|
const newInputValue = inputExpressions[i](scope);
|
|
2434
474
|
if (
|
|
2435
475
|
changed ||
|
|
2436
|
-
// eslint-disable-next-line no-cond-assign
|
|
2437
476
|
(changed = !expressionInputDirtyCheck(
|
|
2438
477
|
newInputValue,
|
|
2439
478
|
oldInputValueOfValues[i],
|
|
@@ -2516,7 +555,8 @@ export function chainInterceptors(first, second) {
|
|
|
2516
555
|
return chainedInterceptor;
|
|
2517
556
|
}
|
|
2518
557
|
|
|
2519
|
-
|
|
558
|
+
/** @private */
|
|
559
|
+
function expressionInputDirtyCheck(
|
|
2520
560
|
newValue,
|
|
2521
561
|
oldValueOfValue,
|
|
2522
562
|
compareObjectIdentity,
|
|
@@ -2541,16 +581,15 @@ export function expressionInputDirtyCheck(
|
|
|
2541
581
|
}
|
|
2542
582
|
|
|
2543
583
|
// Primitive or NaN
|
|
2544
|
-
|
|
584
|
+
|
|
2545
585
|
return (
|
|
2546
586
|
newValue === oldValueOfValue ||
|
|
2547
|
-
// eslint-disable-next-line no-self-compare
|
|
2548
587
|
(newValue !== newValue && oldValueOfValue !== oldValueOfValue)
|
|
2549
588
|
);
|
|
2550
589
|
}
|
|
2551
590
|
|
|
2552
|
-
|
|
2553
|
-
|
|
591
|
+
/** @private */
|
|
592
|
+
function isAllDefined(value) {
|
|
2554
593
|
let allDefined = true;
|
|
2555
594
|
forEach(value, (val) => {
|
|
2556
595
|
if (!isDefined(val)) allDefined = false;
|