@auscope/angular2parse 2.0.0 → 2.0.1
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/auscope-angular2parse.d.ts +5 -0
- package/esm2020/auscope-angular2parse.mjs +5 -0
- package/esm2020/lib/angular/compiler/assertions.mjs +45 -0
- package/esm2020/lib/angular/compiler/ast.mjs +393 -0
- package/esm2020/lib/angular/compiler/chars.mjs +78 -0
- package/esm2020/lib/angular/compiler/interpolation-config.mjs +24 -0
- package/esm2020/lib/angular/compiler/lexer.mjs +344 -0
- package/esm2020/lib/angular/compiler/parser.mjs +721 -0
- package/esm2020/lib/angular/compiler.mjs +7 -0
- package/esm2020/lib/angular/facade/lang.mjs +85 -0
- package/esm2020/lib/angular/facade.mjs +2 -0
- package/esm2020/lib/angular.mjs +3 -0
- package/esm2020/lib/module.mjs +18 -0
- package/esm2020/lib/parse.mjs +66 -0
- package/esm2020/lib/util/binary-operations.mjs +18 -0
- package/esm2020/lib/util/lang.mjs +13 -0
- package/esm2020/lib/util.mjs +3 -0
- package/esm2020/lib/visitors/parse-visitor-compiler.mjs +96 -0
- package/esm2020/lib/visitors/parse-visitor-resolver.mjs +141 -0
- package/esm2020/lib/visitors.mjs +3 -0
- package/esm2020/public-api.mjs +6 -0
- package/fesm2015/auscope-angular2parse.mjs +2035 -0
- package/fesm2015/auscope-angular2parse.mjs.map +1 -0
- package/fesm2020/auscope-angular2parse.mjs +2033 -0
- package/fesm2020/auscope-angular2parse.mjs.map +1 -0
- package/lib/angular/compiler/assertions.d.ts +9 -0
- package/lib/angular/compiler/ast.d.ts +242 -0
- package/lib/angular/compiler/chars.d.ts +69 -0
- package/lib/angular/compiler/interpolation-config.d.ts +14 -0
- package/lib/angular/compiler/lexer.d.ts +44 -0
- package/lib/angular/compiler/parser.d.ts +92 -0
- package/{src/lib/angular/compiler.ts → lib/angular/compiler.d.ts} +1 -1
- package/lib/angular/facade/lang.d.ts +44 -0
- package/lib/angular/facade.d.ts +1 -0
- package/{src/lib/angular.ts → lib/angular.d.ts} +1 -1
- package/lib/module.d.ts +9 -0
- package/lib/parse.d.ts +21 -0
- package/lib/util/binary-operations.d.ts +1 -0
- package/lib/util/lang.d.ts +4 -0
- package/{src/lib/util.ts → lib/util.d.ts} +1 -1
- package/lib/visitors/parse-visitor-compiler.d.ts +23 -0
- package/lib/visitors/parse-visitor-resolver.d.ts +25 -0
- package/lib/visitors.d.ts +2 -0
- package/package.json +41 -18
- package/public-api.d.ts +2 -0
- package/ng-package.json +0 -7
- package/src/lib/angular/compiler/assertions.ts +0 -49
- package/src/lib/angular/compiler/ast.ts +0 -393
- package/src/lib/angular/compiler/chars.ts +0 -89
- package/src/lib/angular/compiler/interpolation-config.ts +0 -25
- package/src/lib/angular/compiler/lexer.ts +0 -383
- package/src/lib/angular/compiler/parser.ts +0 -811
- package/src/lib/angular/facade/lang.ts +0 -124
- package/src/lib/angular/facade.ts +0 -1
- package/src/lib/module.ts +0 -12
- package/src/lib/parse.ts +0 -78
- package/src/lib/util/binary-operations.ts +0 -17
- package/src/lib/util/lang.ts +0 -15
- package/src/lib/visitors/parse-visitor-compiler.ts +0 -149
- package/src/lib/visitors/parse-visitor-resolver.ts +0 -208
- package/src/lib/visitors.ts +0 -2
- package/src/public-api.ts +0 -5
- package/tsconfig.lib.json +0 -20
- package/tsconfig.lib.prod.json +0 -9
|
@@ -0,0 +1,2035 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Injectable, Optional, Inject, NgModule } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
9
|
+
* found in the LICENSE file at https://angular.io/license
|
|
10
|
+
*/
|
|
11
|
+
function getTypeNameForDebugging(type) {
|
|
12
|
+
return type['name'] || typeof type;
|
|
13
|
+
}
|
|
14
|
+
function isPresent$1(obj) {
|
|
15
|
+
return obj != null;
|
|
16
|
+
}
|
|
17
|
+
function isBlank(obj) {
|
|
18
|
+
return obj == null;
|
|
19
|
+
}
|
|
20
|
+
const STRING_MAP_PROTO = Object.getPrototypeOf({});
|
|
21
|
+
function isStrictStringMap(obj) {
|
|
22
|
+
return typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
|
|
23
|
+
}
|
|
24
|
+
function stringify(token) {
|
|
25
|
+
if (typeof token === 'string') {
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
28
|
+
if (token == null) {
|
|
29
|
+
return '' + token;
|
|
30
|
+
}
|
|
31
|
+
if (token.overriddenName) {
|
|
32
|
+
return `${token.overriddenName}`;
|
|
33
|
+
}
|
|
34
|
+
if (token.name) {
|
|
35
|
+
return `${token.name}`;
|
|
36
|
+
}
|
|
37
|
+
const res = token.toString();
|
|
38
|
+
const newLineIndex = res.indexOf('\n');
|
|
39
|
+
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
|
40
|
+
}
|
|
41
|
+
class NumberWrapper {
|
|
42
|
+
static parseIntAutoRadix(text) {
|
|
43
|
+
const result = parseInt(text);
|
|
44
|
+
if (isNaN(result)) {
|
|
45
|
+
throw new Error('Invalid integer literal when parsing ' + text);
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
static isNumeric(value) { return !isNaN(value - parseFloat(value)); }
|
|
50
|
+
}
|
|
51
|
+
// JS has NaN !== NaN
|
|
52
|
+
function looseIdentical(a, b) {
|
|
53
|
+
return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b);
|
|
54
|
+
}
|
|
55
|
+
function isJsObject$1(o) {
|
|
56
|
+
return o !== null && (typeof o === 'function' || typeof o === 'object');
|
|
57
|
+
}
|
|
58
|
+
function print(obj) {
|
|
59
|
+
// tslint:disable-next-line:no-console
|
|
60
|
+
console.log(obj);
|
|
61
|
+
}
|
|
62
|
+
function warn(obj) {
|
|
63
|
+
console.warn(obj);
|
|
64
|
+
}
|
|
65
|
+
function setValueOnPath(global, path, value) {
|
|
66
|
+
const parts = path.split('.');
|
|
67
|
+
let obj = global;
|
|
68
|
+
while (parts.length > 1) {
|
|
69
|
+
const name = parts.shift();
|
|
70
|
+
if (obj.hasOwnProperty(name) && obj[name] != null) {
|
|
71
|
+
obj = obj[name];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
obj = obj[name] = {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (obj === undefined || obj === null) {
|
|
78
|
+
obj = {};
|
|
79
|
+
}
|
|
80
|
+
obj[parts.shift()] = value;
|
|
81
|
+
}
|
|
82
|
+
function isPrimitive(obj) {
|
|
83
|
+
return !isJsObject$1(obj);
|
|
84
|
+
}
|
|
85
|
+
function escapeRegExp(s) {
|
|
86
|
+
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @license
|
|
91
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
92
|
+
*
|
|
93
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
94
|
+
* found in the LICENSE file at https://angular.io/license
|
|
95
|
+
*/
|
|
96
|
+
const isDevMode = () => false;
|
|
97
|
+
function assertArrayOfStrings(identifier, value) {
|
|
98
|
+
if (!isDevMode() || isBlank(value)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!Array.isArray(value)) {
|
|
102
|
+
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
|
103
|
+
}
|
|
104
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
105
|
+
if (typeof value[i] !== 'string') {
|
|
106
|
+
throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const INTERPOLATION_BLACKLIST_REGEXPS = [
|
|
111
|
+
/^\s*$/,
|
|
112
|
+
/[<>]/,
|
|
113
|
+
/^[{}]$/,
|
|
114
|
+
/&(#|[a-z])/i,
|
|
115
|
+
/^\/\//, // comment
|
|
116
|
+
];
|
|
117
|
+
function assertInterpolationSymbols(identifier, value) {
|
|
118
|
+
if (isPresent$1(value) && !(Array.isArray(value) && value.length == 2)) {
|
|
119
|
+
throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
|
|
120
|
+
}
|
|
121
|
+
else if (isDevMode() && !isBlank(value)) {
|
|
122
|
+
const start = value[0];
|
|
123
|
+
const end = value[1];
|
|
124
|
+
// black list checking
|
|
125
|
+
INTERPOLATION_BLACKLIST_REGEXPS.forEach(regexp => {
|
|
126
|
+
if (regexp.test(start) || regexp.test(end)) {
|
|
127
|
+
throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @license
|
|
135
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
136
|
+
*
|
|
137
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
138
|
+
* found in the LICENSE file at https://angular.io/license
|
|
139
|
+
*/
|
|
140
|
+
class ParserError {
|
|
141
|
+
constructor(message, input, errLocation, ctxLocation) {
|
|
142
|
+
this.input = input;
|
|
143
|
+
this.errLocation = errLocation;
|
|
144
|
+
this.ctxLocation = ctxLocation;
|
|
145
|
+
this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
class ParseSpan {
|
|
149
|
+
constructor(start, end) {
|
|
150
|
+
this.start = start;
|
|
151
|
+
this.end = end;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
class AST {
|
|
155
|
+
constructor(span) {
|
|
156
|
+
this.span = span;
|
|
157
|
+
}
|
|
158
|
+
visit(visitor, context = null) { return null; }
|
|
159
|
+
toString() { return 'AST'; }
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Represents a quoted expression of the form:
|
|
163
|
+
*
|
|
164
|
+
* quote = prefix `:` uninterpretedExpression
|
|
165
|
+
* prefix = identifier
|
|
166
|
+
* uninterpretedExpression = arbitrary string
|
|
167
|
+
*
|
|
168
|
+
* A quoted expression is meant to be pre-processed by an AST transformer that
|
|
169
|
+
* converts it into another AST that no longer contains quoted expressions.
|
|
170
|
+
* It is meant to allow third-party developers to extend Angular template
|
|
171
|
+
* expression language. The `uninterpretedExpression` part of the quote is
|
|
172
|
+
* therefore not interpreted by the Angular's own expression parser.
|
|
173
|
+
*/
|
|
174
|
+
class Quote extends AST {
|
|
175
|
+
constructor(span, prefix, uninterpretedExpression, location) {
|
|
176
|
+
super(span);
|
|
177
|
+
this.prefix = prefix;
|
|
178
|
+
this.uninterpretedExpression = uninterpretedExpression;
|
|
179
|
+
this.location = location;
|
|
180
|
+
}
|
|
181
|
+
visit(visitor, context = null) { return visitor.visitQuote(this, context); }
|
|
182
|
+
toString() { return 'Quote'; }
|
|
183
|
+
}
|
|
184
|
+
class EmptyExpr extends AST {
|
|
185
|
+
visit(visitor, context = null) {
|
|
186
|
+
// do nothing
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
class ImplicitReceiver extends AST {
|
|
190
|
+
visit(visitor, context = null) {
|
|
191
|
+
return visitor.visitImplicitReceiver(this, context);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Multiple expressions separated by a semicolon.
|
|
196
|
+
*/
|
|
197
|
+
class Chain extends AST {
|
|
198
|
+
constructor(span, expressions) {
|
|
199
|
+
super(span);
|
|
200
|
+
this.expressions = expressions;
|
|
201
|
+
}
|
|
202
|
+
visit(visitor, context = null) { return visitor.visitChain(this, context); }
|
|
203
|
+
}
|
|
204
|
+
class Conditional extends AST {
|
|
205
|
+
constructor(span, condition, trueExp, falseExp) {
|
|
206
|
+
super(span);
|
|
207
|
+
this.condition = condition;
|
|
208
|
+
this.trueExp = trueExp;
|
|
209
|
+
this.falseExp = falseExp;
|
|
210
|
+
}
|
|
211
|
+
visit(visitor, context = null) {
|
|
212
|
+
return visitor.visitConditional(this, context);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
class PropertyRead extends AST {
|
|
216
|
+
constructor(span, receiver, name) {
|
|
217
|
+
super(span);
|
|
218
|
+
this.receiver = receiver;
|
|
219
|
+
this.name = name;
|
|
220
|
+
}
|
|
221
|
+
visit(visitor, context = null) {
|
|
222
|
+
return visitor.visitPropertyRead(this, context);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
class PropertyWrite extends AST {
|
|
226
|
+
constructor(span, receiver, name, value) {
|
|
227
|
+
super(span);
|
|
228
|
+
this.receiver = receiver;
|
|
229
|
+
this.name = name;
|
|
230
|
+
this.value = value;
|
|
231
|
+
}
|
|
232
|
+
visit(visitor, context = null) {
|
|
233
|
+
return visitor.visitPropertyWrite(this, context);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
class SafePropertyRead extends AST {
|
|
237
|
+
constructor(span, receiver, name) {
|
|
238
|
+
super(span);
|
|
239
|
+
this.receiver = receiver;
|
|
240
|
+
this.name = name;
|
|
241
|
+
}
|
|
242
|
+
visit(visitor, context = null) {
|
|
243
|
+
return visitor.visitSafePropertyRead(this, context);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
class KeyedRead extends AST {
|
|
247
|
+
constructor(span, obj, key) {
|
|
248
|
+
super(span);
|
|
249
|
+
this.obj = obj;
|
|
250
|
+
this.key = key;
|
|
251
|
+
}
|
|
252
|
+
visit(visitor, context = null) {
|
|
253
|
+
return visitor.visitKeyedRead(this, context);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
class KeyedWrite extends AST {
|
|
257
|
+
constructor(span, obj, key, value) {
|
|
258
|
+
super(span);
|
|
259
|
+
this.obj = obj;
|
|
260
|
+
this.key = key;
|
|
261
|
+
this.value = value;
|
|
262
|
+
}
|
|
263
|
+
visit(visitor, context = null) {
|
|
264
|
+
return visitor.visitKeyedWrite(this, context);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
class BindingPipe extends AST {
|
|
268
|
+
constructor(span, exp, name, args) {
|
|
269
|
+
super(span);
|
|
270
|
+
this.exp = exp;
|
|
271
|
+
this.name = name;
|
|
272
|
+
this.args = args;
|
|
273
|
+
}
|
|
274
|
+
visit(visitor, context = null) { return visitor.visitPipe(this, context); }
|
|
275
|
+
}
|
|
276
|
+
class LiteralPrimitive extends AST {
|
|
277
|
+
constructor(span, value) {
|
|
278
|
+
super(span);
|
|
279
|
+
this.value = value;
|
|
280
|
+
}
|
|
281
|
+
visit(visitor, context = null) {
|
|
282
|
+
return visitor.visitLiteralPrimitive(this, context);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
class LiteralArray extends AST {
|
|
286
|
+
constructor(span, expressions) {
|
|
287
|
+
super(span);
|
|
288
|
+
this.expressions = expressions;
|
|
289
|
+
}
|
|
290
|
+
visit(visitor, context = null) {
|
|
291
|
+
return visitor.visitLiteralArray(this, context);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
class LiteralMap extends AST {
|
|
295
|
+
constructor(span, keys, values) {
|
|
296
|
+
super(span);
|
|
297
|
+
this.keys = keys;
|
|
298
|
+
this.values = values;
|
|
299
|
+
}
|
|
300
|
+
visit(visitor, context = null) {
|
|
301
|
+
return visitor.visitLiteralMap(this, context);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
class Interpolation extends AST {
|
|
305
|
+
constructor(span, strings, expressions) {
|
|
306
|
+
super(span);
|
|
307
|
+
this.strings = strings;
|
|
308
|
+
this.expressions = expressions;
|
|
309
|
+
}
|
|
310
|
+
visit(visitor, context = null) {
|
|
311
|
+
return visitor.visitInterpolation(this, context);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
class Binary extends AST {
|
|
315
|
+
constructor(span, operation, left, right) {
|
|
316
|
+
super(span);
|
|
317
|
+
this.operation = operation;
|
|
318
|
+
this.left = left;
|
|
319
|
+
this.right = right;
|
|
320
|
+
}
|
|
321
|
+
visit(visitor, context = null) {
|
|
322
|
+
return visitor.visitBinary(this, context);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
class PrefixNot extends AST {
|
|
326
|
+
constructor(span, expression) {
|
|
327
|
+
super(span);
|
|
328
|
+
this.expression = expression;
|
|
329
|
+
}
|
|
330
|
+
visit(visitor, context = null) {
|
|
331
|
+
return visitor.visitPrefixNot(this, context);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
class MethodCall extends AST {
|
|
335
|
+
constructor(span, receiver, name, args) {
|
|
336
|
+
super(span);
|
|
337
|
+
this.receiver = receiver;
|
|
338
|
+
this.name = name;
|
|
339
|
+
this.args = args;
|
|
340
|
+
}
|
|
341
|
+
visit(visitor, context = null) {
|
|
342
|
+
return visitor.visitMethodCall(this, context);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
class SafeMethodCall extends AST {
|
|
346
|
+
constructor(span, receiver, name, args) {
|
|
347
|
+
super(span);
|
|
348
|
+
this.receiver = receiver;
|
|
349
|
+
this.name = name;
|
|
350
|
+
this.args = args;
|
|
351
|
+
}
|
|
352
|
+
visit(visitor, context = null) {
|
|
353
|
+
return visitor.visitSafeMethodCall(this, context);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
class FunctionCall extends AST {
|
|
357
|
+
constructor(span, target, args) {
|
|
358
|
+
super(span);
|
|
359
|
+
this.target = target;
|
|
360
|
+
this.args = args;
|
|
361
|
+
}
|
|
362
|
+
visit(visitor, context = null) {
|
|
363
|
+
return visitor.visitFunctionCall(this, context);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
class ASTWithSource extends AST {
|
|
367
|
+
constructor(ast, source, location, errors) {
|
|
368
|
+
super(new ParseSpan(0, isBlank(source) ? 0 : source.length));
|
|
369
|
+
this.ast = ast;
|
|
370
|
+
this.source = source;
|
|
371
|
+
this.location = location;
|
|
372
|
+
this.errors = errors;
|
|
373
|
+
}
|
|
374
|
+
visit(visitor, context = null) { return this.ast.visit(visitor, context); }
|
|
375
|
+
toString() { return `${this.source} in ${this.location}`; }
|
|
376
|
+
}
|
|
377
|
+
class TemplateBinding {
|
|
378
|
+
constructor(span, key, keyIsVar, name, expression) {
|
|
379
|
+
this.span = span;
|
|
380
|
+
this.key = key;
|
|
381
|
+
this.keyIsVar = keyIsVar;
|
|
382
|
+
this.name = name;
|
|
383
|
+
this.expression = expression;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
class RecursiveAstVisitor {
|
|
387
|
+
visitBinary(ast, context) {
|
|
388
|
+
ast.left.visit(this);
|
|
389
|
+
ast.right.visit(this);
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
visitChain(ast, context) { return this.visitAll(ast.expressions, context); }
|
|
393
|
+
visitConditional(ast, context) {
|
|
394
|
+
ast.condition.visit(this);
|
|
395
|
+
ast.trueExp.visit(this);
|
|
396
|
+
ast.falseExp.visit(this);
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
visitPipe(ast, context) {
|
|
400
|
+
ast.exp.visit(this);
|
|
401
|
+
this.visitAll(ast.args, context);
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
visitFunctionCall(ast, context) {
|
|
405
|
+
ast.target.visit(this);
|
|
406
|
+
this.visitAll(ast.args, context);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
visitImplicitReceiver(ast, context) { return null; }
|
|
410
|
+
visitInterpolation(ast, context) {
|
|
411
|
+
return this.visitAll(ast.expressions, context);
|
|
412
|
+
}
|
|
413
|
+
visitKeyedRead(ast, context) {
|
|
414
|
+
ast.obj.visit(this);
|
|
415
|
+
ast.key.visit(this);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
visitKeyedWrite(ast, context) {
|
|
419
|
+
ast.obj.visit(this);
|
|
420
|
+
ast.key.visit(this);
|
|
421
|
+
ast.value.visit(this);
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
visitLiteralArray(ast, context) {
|
|
425
|
+
return this.visitAll(ast.expressions, context);
|
|
426
|
+
}
|
|
427
|
+
visitLiteralMap(ast, context) { return this.visitAll(ast.values, context); }
|
|
428
|
+
visitLiteralPrimitive(ast, context) { return null; }
|
|
429
|
+
visitMethodCall(ast, context) {
|
|
430
|
+
ast.receiver.visit(this);
|
|
431
|
+
return this.visitAll(ast.args, context);
|
|
432
|
+
}
|
|
433
|
+
visitPrefixNot(ast, context) {
|
|
434
|
+
ast.expression.visit(this);
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
visitPropertyRead(ast, context) {
|
|
438
|
+
ast.receiver.visit(this);
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
visitPropertyWrite(ast, context) {
|
|
442
|
+
ast.receiver.visit(this);
|
|
443
|
+
ast.value.visit(this);
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
visitSafePropertyRead(ast, context) {
|
|
447
|
+
ast.receiver.visit(this);
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
visitSafeMethodCall(ast, context) {
|
|
451
|
+
ast.receiver.visit(this);
|
|
452
|
+
return this.visitAll(ast.args, context);
|
|
453
|
+
}
|
|
454
|
+
visitAll(asts, context) {
|
|
455
|
+
asts.forEach(ast => ast.visit(this, context));
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
visitQuote(ast, context) { return null; }
|
|
459
|
+
}
|
|
460
|
+
class AstTransformer {
|
|
461
|
+
visitImplicitReceiver(ast, context) { return ast; }
|
|
462
|
+
visitInterpolation(ast, context) {
|
|
463
|
+
return new Interpolation(ast.span, ast.strings, this.visitAll(ast.expressions));
|
|
464
|
+
}
|
|
465
|
+
visitLiteralPrimitive(ast, context) {
|
|
466
|
+
return new LiteralPrimitive(ast.span, ast.value);
|
|
467
|
+
}
|
|
468
|
+
visitPropertyRead(ast, context) {
|
|
469
|
+
return new PropertyRead(ast.span, ast.receiver.visit(this), ast.name);
|
|
470
|
+
}
|
|
471
|
+
visitPropertyWrite(ast, context) {
|
|
472
|
+
return new PropertyWrite(ast.span, ast.receiver.visit(this), ast.name, ast.value);
|
|
473
|
+
}
|
|
474
|
+
visitSafePropertyRead(ast, context) {
|
|
475
|
+
return new SafePropertyRead(ast.span, ast.receiver.visit(this), ast.name);
|
|
476
|
+
}
|
|
477
|
+
visitMethodCall(ast, context) {
|
|
478
|
+
return new MethodCall(ast.span, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
|
479
|
+
}
|
|
480
|
+
visitSafeMethodCall(ast, context) {
|
|
481
|
+
return new SafeMethodCall(ast.span, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
|
482
|
+
}
|
|
483
|
+
visitFunctionCall(ast, context) {
|
|
484
|
+
return new FunctionCall(ast.span, ast.target.visit(this), this.visitAll(ast.args));
|
|
485
|
+
}
|
|
486
|
+
visitLiteralArray(ast, context) {
|
|
487
|
+
return new LiteralArray(ast.span, this.visitAll(ast.expressions));
|
|
488
|
+
}
|
|
489
|
+
visitLiteralMap(ast, context) {
|
|
490
|
+
return new LiteralMap(ast.span, ast.keys, this.visitAll(ast.values));
|
|
491
|
+
}
|
|
492
|
+
visitBinary(ast, context) {
|
|
493
|
+
return new Binary(ast.span, ast.operation, ast.left.visit(this), ast.right.visit(this));
|
|
494
|
+
}
|
|
495
|
+
visitPrefixNot(ast, context) {
|
|
496
|
+
return new PrefixNot(ast.span, ast.expression.visit(this));
|
|
497
|
+
}
|
|
498
|
+
visitConditional(ast, context) {
|
|
499
|
+
return new Conditional(ast.span, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
|
|
500
|
+
}
|
|
501
|
+
visitPipe(ast, context) {
|
|
502
|
+
return new BindingPipe(ast.span, ast.exp.visit(this), ast.name, this.visitAll(ast.args));
|
|
503
|
+
}
|
|
504
|
+
visitKeyedRead(ast, context) {
|
|
505
|
+
return new KeyedRead(ast.span, ast.obj.visit(this), ast.key.visit(this));
|
|
506
|
+
}
|
|
507
|
+
visitKeyedWrite(ast, context) {
|
|
508
|
+
return new KeyedWrite(ast.span, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
|
509
|
+
}
|
|
510
|
+
visitAll(asts) {
|
|
511
|
+
const res = new Array(asts.length);
|
|
512
|
+
for (let i = 0; i < asts.length; ++i) {
|
|
513
|
+
res[i] = asts[i].visit(this);
|
|
514
|
+
}
|
|
515
|
+
return res;
|
|
516
|
+
}
|
|
517
|
+
visitChain(ast, context) {
|
|
518
|
+
return new Chain(ast.span, this.visitAll(ast.expressions));
|
|
519
|
+
}
|
|
520
|
+
visitQuote(ast, context) {
|
|
521
|
+
return new Quote(ast.span, ast.prefix, ast.uninterpretedExpression, ast.location);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* @license
|
|
527
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
528
|
+
*
|
|
529
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
530
|
+
* found in the LICENSE file at https://angular.io/license
|
|
531
|
+
*/
|
|
532
|
+
const $EOF = 0;
|
|
533
|
+
const $TAB = 9;
|
|
534
|
+
const $LF = 10;
|
|
535
|
+
const $VTAB = 11;
|
|
536
|
+
const $FF = 12;
|
|
537
|
+
const $CR = 13;
|
|
538
|
+
const $SPACE = 32;
|
|
539
|
+
const $BANG = 33;
|
|
540
|
+
const $DQ = 34;
|
|
541
|
+
const $HASH = 35;
|
|
542
|
+
const $$ = 36;
|
|
543
|
+
const $PERCENT = 37;
|
|
544
|
+
const $AMPERSAND = 38;
|
|
545
|
+
const $SQ = 39;
|
|
546
|
+
const $LPAREN = 40;
|
|
547
|
+
const $RPAREN = 41;
|
|
548
|
+
const $STAR = 42;
|
|
549
|
+
const $PLUS = 43;
|
|
550
|
+
const $COMMA = 44;
|
|
551
|
+
const $MINUS = 45;
|
|
552
|
+
const $PERIOD = 46;
|
|
553
|
+
const $SLASH = 47;
|
|
554
|
+
const $COLON = 58;
|
|
555
|
+
const $SEMICOLON = 59;
|
|
556
|
+
const $LT = 60;
|
|
557
|
+
const $EQ = 61;
|
|
558
|
+
const $GT = 62;
|
|
559
|
+
const $QUESTION = 63;
|
|
560
|
+
const $0 = 48;
|
|
561
|
+
const $9 = 57;
|
|
562
|
+
const $A = 65;
|
|
563
|
+
const $E = 69;
|
|
564
|
+
const $F = 70;
|
|
565
|
+
const $X = 88;
|
|
566
|
+
const $Z = 90;
|
|
567
|
+
const $LBRACKET = 91;
|
|
568
|
+
const $BACKSLASH = 92;
|
|
569
|
+
const $RBRACKET = 93;
|
|
570
|
+
const $CARET = 94;
|
|
571
|
+
const $_ = 95;
|
|
572
|
+
const $a = 97;
|
|
573
|
+
const $e = 101;
|
|
574
|
+
const $f = 102;
|
|
575
|
+
const $n = 110;
|
|
576
|
+
const $r = 114;
|
|
577
|
+
const $t = 116;
|
|
578
|
+
const $u = 117;
|
|
579
|
+
const $v = 118;
|
|
580
|
+
const $x = 120;
|
|
581
|
+
const $z = 122;
|
|
582
|
+
const $LBRACE = 123;
|
|
583
|
+
const $BAR = 124;
|
|
584
|
+
const $RBRACE = 125;
|
|
585
|
+
const $NBSP = 160;
|
|
586
|
+
const $PIPE = 124;
|
|
587
|
+
const $TILDA = 126;
|
|
588
|
+
const $AT = 64;
|
|
589
|
+
const $BT = 96;
|
|
590
|
+
function isWhitespace(code) {
|
|
591
|
+
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
|
592
|
+
}
|
|
593
|
+
function isDigit(code) {
|
|
594
|
+
return $0 <= code && code <= $9;
|
|
595
|
+
}
|
|
596
|
+
function isAsciiLetter(code) {
|
|
597
|
+
return code >= $a && code <= $z || code >= $A && code <= $Z;
|
|
598
|
+
}
|
|
599
|
+
function isAsciiHexDigit(code) {
|
|
600
|
+
return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* @license
|
|
605
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
606
|
+
*
|
|
607
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
608
|
+
* found in the LICENSE file at https://angular.io/license
|
|
609
|
+
*/
|
|
610
|
+
class InterpolationConfig {
|
|
611
|
+
constructor(start, end) {
|
|
612
|
+
this.start = start;
|
|
613
|
+
this.end = end;
|
|
614
|
+
}
|
|
615
|
+
static fromArray(markers) {
|
|
616
|
+
if (!markers) {
|
|
617
|
+
return DEFAULT_INTERPOLATION_CONFIG;
|
|
618
|
+
}
|
|
619
|
+
assertInterpolationSymbols('interpolation', markers);
|
|
620
|
+
return new InterpolationConfig(markers[0], markers[1]);
|
|
621
|
+
}
|
|
622
|
+
;
|
|
623
|
+
}
|
|
624
|
+
const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* @license
|
|
628
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
629
|
+
*
|
|
630
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
631
|
+
* found in the LICENSE file at https://angular.io/license
|
|
632
|
+
*/
|
|
633
|
+
var TokenType;
|
|
634
|
+
(function (TokenType) {
|
|
635
|
+
TokenType[TokenType["Character"] = 0] = "Character";
|
|
636
|
+
TokenType[TokenType["Identifier"] = 1] = "Identifier";
|
|
637
|
+
TokenType[TokenType["Keyword"] = 2] = "Keyword";
|
|
638
|
+
TokenType[TokenType["String"] = 3] = "String";
|
|
639
|
+
TokenType[TokenType["Operator"] = 4] = "Operator";
|
|
640
|
+
TokenType[TokenType["Number"] = 5] = "Number";
|
|
641
|
+
TokenType[TokenType["Error"] = 6] = "Error";
|
|
642
|
+
})(TokenType || (TokenType = {}));
|
|
643
|
+
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
|
644
|
+
class Lexer {
|
|
645
|
+
tokenize(text) {
|
|
646
|
+
const scanner = new _Scanner(text);
|
|
647
|
+
const tokens = [];
|
|
648
|
+
let token = scanner.scanToken();
|
|
649
|
+
while (token != null) {
|
|
650
|
+
tokens.push(token);
|
|
651
|
+
token = scanner.scanToken();
|
|
652
|
+
}
|
|
653
|
+
return tokens;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
class Token {
|
|
657
|
+
constructor(index, type, numValue, strValue) {
|
|
658
|
+
this.index = index;
|
|
659
|
+
this.type = type;
|
|
660
|
+
this.numValue = numValue;
|
|
661
|
+
this.strValue = strValue;
|
|
662
|
+
}
|
|
663
|
+
isCharacter(code) {
|
|
664
|
+
return this.type == TokenType.Character && this.numValue == code;
|
|
665
|
+
}
|
|
666
|
+
isNumber() { return this.type == TokenType.Number; }
|
|
667
|
+
isString() { return this.type == TokenType.String; }
|
|
668
|
+
isOperator(operater) {
|
|
669
|
+
return this.type == TokenType.Operator && this.strValue == operater;
|
|
670
|
+
}
|
|
671
|
+
isIdentifier() { return this.type == TokenType.Identifier; }
|
|
672
|
+
isKeyword() { return this.type == TokenType.Keyword; }
|
|
673
|
+
isKeywordLet() { return this.type == TokenType.Keyword && this.strValue == 'let'; }
|
|
674
|
+
isKeywordNull() { return this.type == TokenType.Keyword && this.strValue == 'null'; }
|
|
675
|
+
isKeywordUndefined() {
|
|
676
|
+
return this.type == TokenType.Keyword && this.strValue == 'undefined';
|
|
677
|
+
}
|
|
678
|
+
isKeywordTrue() { return this.type == TokenType.Keyword && this.strValue == 'true'; }
|
|
679
|
+
isKeywordFalse() { return this.type == TokenType.Keyword && this.strValue == 'false'; }
|
|
680
|
+
isKeywordThis() { return this.type == TokenType.Keyword && this.strValue == 'this'; }
|
|
681
|
+
isError() { return this.type == TokenType.Error; }
|
|
682
|
+
toNumber() { return this.type == TokenType.Number ? this.numValue : -1; }
|
|
683
|
+
toString() {
|
|
684
|
+
switch (this.type) {
|
|
685
|
+
case TokenType.Character:
|
|
686
|
+
case TokenType.Identifier:
|
|
687
|
+
case TokenType.Keyword:
|
|
688
|
+
case TokenType.Operator:
|
|
689
|
+
case TokenType.String:
|
|
690
|
+
case TokenType.Error:
|
|
691
|
+
return this.strValue;
|
|
692
|
+
case TokenType.Number:
|
|
693
|
+
return this.numValue.toString();
|
|
694
|
+
default:
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function newCharacterToken(index, code) {
|
|
700
|
+
return new Token(index, TokenType.Character, code, String.fromCharCode(code));
|
|
701
|
+
}
|
|
702
|
+
function newIdentifierToken(index, text) {
|
|
703
|
+
return new Token(index, TokenType.Identifier, 0, text);
|
|
704
|
+
}
|
|
705
|
+
function newKeywordToken(index, text) {
|
|
706
|
+
return new Token(index, TokenType.Keyword, 0, text);
|
|
707
|
+
}
|
|
708
|
+
function newOperatorToken(index, text) {
|
|
709
|
+
return new Token(index, TokenType.Operator, 0, text);
|
|
710
|
+
}
|
|
711
|
+
function newStringToken(index, text) {
|
|
712
|
+
return new Token(index, TokenType.String, 0, text);
|
|
713
|
+
}
|
|
714
|
+
function newNumberToken(index, n) {
|
|
715
|
+
return new Token(index, TokenType.Number, n, '');
|
|
716
|
+
}
|
|
717
|
+
function newErrorToken(index, message) {
|
|
718
|
+
return new Token(index, TokenType.Error, 0, message);
|
|
719
|
+
}
|
|
720
|
+
const EOF = new Token(-1, TokenType.Character, 0, '');
|
|
721
|
+
class _Scanner {
|
|
722
|
+
constructor(input) {
|
|
723
|
+
this.input = input;
|
|
724
|
+
this.peek = 0;
|
|
725
|
+
this.index = -1;
|
|
726
|
+
this.length = input.length;
|
|
727
|
+
this.advance();
|
|
728
|
+
}
|
|
729
|
+
advance() {
|
|
730
|
+
this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
|
|
731
|
+
}
|
|
732
|
+
scanToken() {
|
|
733
|
+
const input = this.input, length = this.length;
|
|
734
|
+
let peek = this.peek, index = this.index;
|
|
735
|
+
// Skip whitespace.
|
|
736
|
+
while (peek <= $SPACE) {
|
|
737
|
+
if (++index >= length) {
|
|
738
|
+
peek = $EOF;
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
peek = input.charCodeAt(index);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
this.peek = peek;
|
|
746
|
+
this.index = index;
|
|
747
|
+
if (index >= length) {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
// Handle identifiers and numbers.
|
|
751
|
+
if (isIdentifierStart(peek))
|
|
752
|
+
return this.scanIdentifier();
|
|
753
|
+
if (isDigit(peek))
|
|
754
|
+
return this.scanNumber(index);
|
|
755
|
+
const start = index;
|
|
756
|
+
switch (peek) {
|
|
757
|
+
case $PERIOD:
|
|
758
|
+
this.advance();
|
|
759
|
+
return isDigit(this.peek) ? this.scanNumber(start) :
|
|
760
|
+
newCharacterToken(start, $PERIOD);
|
|
761
|
+
case $LPAREN:
|
|
762
|
+
case $RPAREN:
|
|
763
|
+
case $LBRACE:
|
|
764
|
+
case $RBRACE:
|
|
765
|
+
case $LBRACKET:
|
|
766
|
+
case $RBRACKET:
|
|
767
|
+
case $COMMA:
|
|
768
|
+
case $COLON:
|
|
769
|
+
case $SEMICOLON:
|
|
770
|
+
return this.scanCharacter(start, peek);
|
|
771
|
+
case $SQ:
|
|
772
|
+
case $DQ:
|
|
773
|
+
return this.scanString();
|
|
774
|
+
case $HASH:
|
|
775
|
+
case $PLUS:
|
|
776
|
+
case $MINUS:
|
|
777
|
+
case $STAR:
|
|
778
|
+
case $SLASH:
|
|
779
|
+
case $PERCENT:
|
|
780
|
+
case $CARET:
|
|
781
|
+
return this.scanOperator(start, String.fromCharCode(peek));
|
|
782
|
+
case $QUESTION:
|
|
783
|
+
return this.scanComplexOperator(start, '?', $PERIOD, '.');
|
|
784
|
+
case $LT:
|
|
785
|
+
case $GT:
|
|
786
|
+
return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
|
|
787
|
+
case $BANG:
|
|
788
|
+
case $EQ:
|
|
789
|
+
return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
|
|
790
|
+
case $AMPERSAND:
|
|
791
|
+
return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
|
|
792
|
+
case $BAR:
|
|
793
|
+
return this.scanComplexOperator(start, '|', $BAR, '|');
|
|
794
|
+
case $NBSP:
|
|
795
|
+
while (isWhitespace(this.peek))
|
|
796
|
+
this.advance();
|
|
797
|
+
return this.scanToken();
|
|
798
|
+
}
|
|
799
|
+
this.advance();
|
|
800
|
+
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
|
801
|
+
}
|
|
802
|
+
scanCharacter(start, code) {
|
|
803
|
+
this.advance();
|
|
804
|
+
return newCharacterToken(start, code);
|
|
805
|
+
}
|
|
806
|
+
scanOperator(start, str) {
|
|
807
|
+
this.advance();
|
|
808
|
+
return newOperatorToken(start, str);
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Tokenize a 2/3 char long operator
|
|
812
|
+
*
|
|
813
|
+
* @param start start index in the expression
|
|
814
|
+
* @param one first symbol (always part of the operator)
|
|
815
|
+
* @param twoCode code point for the second symbol
|
|
816
|
+
* @param two second symbol (part of the operator when the second code point matches)
|
|
817
|
+
* @param threeCode code point for the third symbol
|
|
818
|
+
* @param three third symbol (part of the operator when provided and matches source expression)
|
|
819
|
+
* @returns {Token}
|
|
820
|
+
*/
|
|
821
|
+
scanComplexOperator(start, one, twoCode, two, threeCode, three) {
|
|
822
|
+
this.advance();
|
|
823
|
+
let str = one;
|
|
824
|
+
if (this.peek == twoCode) {
|
|
825
|
+
this.advance();
|
|
826
|
+
str += two;
|
|
827
|
+
}
|
|
828
|
+
if (threeCode != null && this.peek == threeCode) {
|
|
829
|
+
this.advance();
|
|
830
|
+
str += three;
|
|
831
|
+
}
|
|
832
|
+
return newOperatorToken(start, str);
|
|
833
|
+
}
|
|
834
|
+
scanIdentifier() {
|
|
835
|
+
const start = this.index;
|
|
836
|
+
this.advance();
|
|
837
|
+
while (isIdentifierPart(this.peek))
|
|
838
|
+
this.advance();
|
|
839
|
+
const str = this.input.substring(start, this.index);
|
|
840
|
+
return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, str) :
|
|
841
|
+
newIdentifierToken(start, str);
|
|
842
|
+
}
|
|
843
|
+
scanNumber(start) {
|
|
844
|
+
let simple = (this.index === start);
|
|
845
|
+
this.advance(); // Skip initial digit.
|
|
846
|
+
while (true) {
|
|
847
|
+
if (isDigit(this.peek)) {
|
|
848
|
+
// Do nothing.
|
|
849
|
+
}
|
|
850
|
+
else if (this.peek == $PERIOD) {
|
|
851
|
+
simple = false;
|
|
852
|
+
}
|
|
853
|
+
else if (isExponentStart(this.peek)) {
|
|
854
|
+
this.advance();
|
|
855
|
+
if (isExponentSign(this.peek))
|
|
856
|
+
this.advance();
|
|
857
|
+
if (!isDigit(this.peek))
|
|
858
|
+
return this.error('Invalid exponent', -1);
|
|
859
|
+
simple = false;
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
this.advance();
|
|
865
|
+
}
|
|
866
|
+
const str = this.input.substring(start, this.index);
|
|
867
|
+
const value = simple ? NumberWrapper.parseIntAutoRadix(str) : parseFloat(str);
|
|
868
|
+
return newNumberToken(start, value);
|
|
869
|
+
}
|
|
870
|
+
scanString() {
|
|
871
|
+
const start = this.index;
|
|
872
|
+
const quote = this.peek;
|
|
873
|
+
this.advance(); // Skip initial quote.
|
|
874
|
+
let buffer = '';
|
|
875
|
+
let marker = this.index;
|
|
876
|
+
const input = this.input;
|
|
877
|
+
while (this.peek != quote) {
|
|
878
|
+
if (this.peek == $BACKSLASH) {
|
|
879
|
+
buffer += input.substring(marker, this.index);
|
|
880
|
+
this.advance();
|
|
881
|
+
let unescapedCode;
|
|
882
|
+
// Workaround for TS2.1-introduced type strictness
|
|
883
|
+
this.peek = this.peek;
|
|
884
|
+
if (this.peek == $u) {
|
|
885
|
+
// 4 character hex code for unicode character.
|
|
886
|
+
const hex = input.substring(this.index + 1, this.index + 5);
|
|
887
|
+
if (/^[0-9a-f]+$/i.test(hex)) {
|
|
888
|
+
unescapedCode = parseInt(hex, 16);
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
|
892
|
+
}
|
|
893
|
+
for (let i = 0; i < 5; i++) {
|
|
894
|
+
this.advance();
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
unescapedCode = unescape(this.peek);
|
|
899
|
+
this.advance();
|
|
900
|
+
}
|
|
901
|
+
buffer += String.fromCharCode(unescapedCode);
|
|
902
|
+
marker = this.index;
|
|
903
|
+
}
|
|
904
|
+
else if (this.peek == $EOF) {
|
|
905
|
+
return this.error('Unterminated quote', 0);
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
this.advance();
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const last = input.substring(marker, this.index);
|
|
912
|
+
this.advance(); // Skip terminating quote.
|
|
913
|
+
return newStringToken(start, buffer + last);
|
|
914
|
+
}
|
|
915
|
+
error(message, offset) {
|
|
916
|
+
const position = this.index + offset;
|
|
917
|
+
return newErrorToken(position, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function isIdentifierStart(code) {
|
|
921
|
+
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
|
|
922
|
+
(code == $_) || (code == $$);
|
|
923
|
+
}
|
|
924
|
+
function isIdentifier(input) {
|
|
925
|
+
if (input.length == 0)
|
|
926
|
+
return false;
|
|
927
|
+
const scanner = new _Scanner(input);
|
|
928
|
+
if (!isIdentifierStart(scanner.peek))
|
|
929
|
+
return false;
|
|
930
|
+
scanner.advance();
|
|
931
|
+
while (scanner.peek !== $EOF) {
|
|
932
|
+
if (!isIdentifierPart(scanner.peek))
|
|
933
|
+
return false;
|
|
934
|
+
scanner.advance();
|
|
935
|
+
}
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
function isIdentifierPart(code) {
|
|
939
|
+
return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
|
|
940
|
+
(code == $$);
|
|
941
|
+
}
|
|
942
|
+
function isExponentStart(code) {
|
|
943
|
+
return code == $e || code == $E;
|
|
944
|
+
}
|
|
945
|
+
function isExponentSign(code) {
|
|
946
|
+
return code == $MINUS || code == $PLUS;
|
|
947
|
+
}
|
|
948
|
+
function isQuote(code) {
|
|
949
|
+
return code === $SQ || code === $DQ || code === $BT;
|
|
950
|
+
}
|
|
951
|
+
function unescape(code) {
|
|
952
|
+
switch (code) {
|
|
953
|
+
case $n:
|
|
954
|
+
return $LF;
|
|
955
|
+
case $f:
|
|
956
|
+
return $FF;
|
|
957
|
+
case $r:
|
|
958
|
+
return $CR;
|
|
959
|
+
case $t:
|
|
960
|
+
return $TAB;
|
|
961
|
+
case $v:
|
|
962
|
+
return $VTAB;
|
|
963
|
+
default:
|
|
964
|
+
return code;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* @license
|
|
970
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
971
|
+
*
|
|
972
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
973
|
+
* found in the LICENSE file at https://angular.io/license
|
|
974
|
+
*/
|
|
975
|
+
class SplitInterpolation {
|
|
976
|
+
constructor(strings, expressions, offsets) {
|
|
977
|
+
this.strings = strings;
|
|
978
|
+
this.expressions = expressions;
|
|
979
|
+
this.offsets = offsets;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
class TemplateBindingParseResult {
|
|
983
|
+
constructor(templateBindings, warnings, errors) {
|
|
984
|
+
this.templateBindings = templateBindings;
|
|
985
|
+
this.warnings = warnings;
|
|
986
|
+
this.errors = errors;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
function _createInterpolateRegExp(config) {
|
|
990
|
+
const pattern = escapeRegExp(config.start) + '([\\s\\S]*?)' + escapeRegExp(config.end);
|
|
991
|
+
return new RegExp(pattern, 'g');
|
|
992
|
+
}
|
|
993
|
+
class Parser {
|
|
994
|
+
constructor(_lexer) {
|
|
995
|
+
this._lexer = _lexer;
|
|
996
|
+
this.errors = [];
|
|
997
|
+
}
|
|
998
|
+
parseAction(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
999
|
+
this._checkNoInterpolation(input, location, interpolationConfig);
|
|
1000
|
+
const sourceToLex = this._stripComments(input);
|
|
1001
|
+
const tokens = this._lexer.tokenize(this._stripComments(input));
|
|
1002
|
+
const ast = new _ParseAST(input, location, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
|
|
1003
|
+
.parseChain();
|
|
1004
|
+
return new ASTWithSource(ast, input, location, this.errors);
|
|
1005
|
+
}
|
|
1006
|
+
parseBinding(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
1007
|
+
const ast = this._parseBindingAst(input, location, interpolationConfig);
|
|
1008
|
+
return new ASTWithSource(ast, input, location, this.errors);
|
|
1009
|
+
}
|
|
1010
|
+
parseSimpleBinding(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
1011
|
+
const ast = this._parseBindingAst(input, location, interpolationConfig);
|
|
1012
|
+
const errors = SimpleExpressionChecker.check(ast);
|
|
1013
|
+
if (errors.length > 0) {
|
|
1014
|
+
this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
|
|
1015
|
+
}
|
|
1016
|
+
return new ASTWithSource(ast, input, location, this.errors);
|
|
1017
|
+
}
|
|
1018
|
+
_reportError(message, input, errLocation, ctxLocation) {
|
|
1019
|
+
this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
|
|
1020
|
+
}
|
|
1021
|
+
_parseBindingAst(input, location, interpolationConfig) {
|
|
1022
|
+
// Quotes expressions use 3rd-party expression language. We don't want to use
|
|
1023
|
+
// our lexer or parser for that, so we check for that ahead of time.
|
|
1024
|
+
const quote = this._parseQuote(input, location);
|
|
1025
|
+
if (isPresent$1(quote)) {
|
|
1026
|
+
return quote;
|
|
1027
|
+
}
|
|
1028
|
+
this._checkNoInterpolation(input, location, interpolationConfig);
|
|
1029
|
+
const sourceToLex = this._stripComments(input);
|
|
1030
|
+
const tokens = this._lexer.tokenize(sourceToLex);
|
|
1031
|
+
return new _ParseAST(input, location, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
|
|
1032
|
+
.parseChain();
|
|
1033
|
+
}
|
|
1034
|
+
_parseQuote(input, location) {
|
|
1035
|
+
if (isBlank(input))
|
|
1036
|
+
return null;
|
|
1037
|
+
const prefixSeparatorIndex = input.indexOf(':');
|
|
1038
|
+
if (prefixSeparatorIndex == -1)
|
|
1039
|
+
return null;
|
|
1040
|
+
const prefix = input.substring(0, prefixSeparatorIndex).trim();
|
|
1041
|
+
if (!isIdentifier(prefix))
|
|
1042
|
+
return null;
|
|
1043
|
+
const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
|
|
1044
|
+
return new Quote(new ParseSpan(0, input.length), prefix, uninterpretedExpression, location);
|
|
1045
|
+
}
|
|
1046
|
+
parseTemplateBindings(prefixToken, input, location) {
|
|
1047
|
+
const tokens = this._lexer.tokenize(input);
|
|
1048
|
+
if (prefixToken) {
|
|
1049
|
+
// Prefix the tokens with the tokens from prefixToken but have them take no space (0 index).
|
|
1050
|
+
const prefixTokens = this._lexer.tokenize(prefixToken).map(t => {
|
|
1051
|
+
t.index = 0;
|
|
1052
|
+
return t;
|
|
1053
|
+
});
|
|
1054
|
+
tokens.unshift(...prefixTokens);
|
|
1055
|
+
}
|
|
1056
|
+
return new _ParseAST(input, location, tokens, input.length, false, this.errors, 0)
|
|
1057
|
+
.parseTemplateBindings();
|
|
1058
|
+
}
|
|
1059
|
+
parseInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
1060
|
+
const split = this.splitInterpolation(input, location, interpolationConfig);
|
|
1061
|
+
if (split == null)
|
|
1062
|
+
return null;
|
|
1063
|
+
const expressions = [];
|
|
1064
|
+
for (let i = 0; i < split.expressions.length; ++i) {
|
|
1065
|
+
const expressionText = split.expressions[i];
|
|
1066
|
+
const sourceToLex = this._stripComments(expressionText);
|
|
1067
|
+
const tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
|
1068
|
+
const ast = new _ParseAST(input, location, tokens, sourceToLex.length, false, this.errors, split.offsets[i] + (expressionText.length - sourceToLex.length))
|
|
1069
|
+
.parseChain();
|
|
1070
|
+
expressions.push(ast);
|
|
1071
|
+
}
|
|
1072
|
+
return new ASTWithSource(new Interpolation(new ParseSpan(0, isBlank(input) ? 0 : input.length), split.strings, expressions), input, location, this.errors);
|
|
1073
|
+
}
|
|
1074
|
+
splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
1075
|
+
const regexp = _createInterpolateRegExp(interpolationConfig);
|
|
1076
|
+
const parts = input.split(regexp);
|
|
1077
|
+
if (parts.length <= 1) {
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
1080
|
+
const strings = [];
|
|
1081
|
+
const expressions = [];
|
|
1082
|
+
const offsets = [];
|
|
1083
|
+
let offset = 0;
|
|
1084
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1085
|
+
const part = parts[i];
|
|
1086
|
+
if (i % 2 === 0) {
|
|
1087
|
+
// fixed string
|
|
1088
|
+
strings.push(part);
|
|
1089
|
+
offset += part.length;
|
|
1090
|
+
}
|
|
1091
|
+
else if (part.trim().length > 0) {
|
|
1092
|
+
offset += interpolationConfig.start.length;
|
|
1093
|
+
expressions.push(part);
|
|
1094
|
+
offsets.push(offset);
|
|
1095
|
+
offset += part.length + interpolationConfig.end.length;
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${this._findInterpolationErrorColumn(parts, i, interpolationConfig)} in`, location);
|
|
1099
|
+
expressions.push('$implict');
|
|
1100
|
+
offsets.push(offset);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return new SplitInterpolation(strings, expressions, offsets);
|
|
1104
|
+
}
|
|
1105
|
+
wrapLiteralPrimitive(input, location) {
|
|
1106
|
+
return new ASTWithSource(new LiteralPrimitive(new ParseSpan(0, isBlank(input) ? 0 : input.length), input), input, location, this.errors);
|
|
1107
|
+
}
|
|
1108
|
+
_stripComments(input) {
|
|
1109
|
+
const i = this._commentStart(input);
|
|
1110
|
+
return isPresent$1(i) ? input.substring(0, i).trim() : input;
|
|
1111
|
+
}
|
|
1112
|
+
_commentStart(input) {
|
|
1113
|
+
let outerQuote = null;
|
|
1114
|
+
for (let i = 0; i < input.length - 1; i++) {
|
|
1115
|
+
const char = input.charCodeAt(i);
|
|
1116
|
+
const nextChar = input.charCodeAt(i + 1);
|
|
1117
|
+
if (char === $SLASH && nextChar == $SLASH && isBlank(outerQuote))
|
|
1118
|
+
return i;
|
|
1119
|
+
if (outerQuote === char) {
|
|
1120
|
+
outerQuote = null;
|
|
1121
|
+
}
|
|
1122
|
+
else if (isBlank(outerQuote) && isQuote(char)) {
|
|
1123
|
+
outerQuote = char;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
_checkNoInterpolation(input, location, interpolationConfig) {
|
|
1129
|
+
const regexp = _createInterpolateRegExp(interpolationConfig);
|
|
1130
|
+
const parts = input.split(regexp);
|
|
1131
|
+
if (parts.length > 1) {
|
|
1132
|
+
this._reportError(`Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`, input, `at column ${this._findInterpolationErrorColumn(parts, 1, interpolationConfig)} in`, location);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
_findInterpolationErrorColumn(parts, partInErrIdx, interpolationConfig) {
|
|
1136
|
+
let errLocation = '';
|
|
1137
|
+
for (let j = 0; j < partInErrIdx; j++) {
|
|
1138
|
+
errLocation += j % 2 === 0 ?
|
|
1139
|
+
parts[j] :
|
|
1140
|
+
`${interpolationConfig.start}${parts[j]}${interpolationConfig.end}`;
|
|
1141
|
+
}
|
|
1142
|
+
return errLocation.length;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
class _ParseAST {
|
|
1146
|
+
constructor(input, location, tokens, inputLength, parseAction, errors, offset) {
|
|
1147
|
+
this.input = input;
|
|
1148
|
+
this.location = location;
|
|
1149
|
+
this.tokens = tokens;
|
|
1150
|
+
this.inputLength = inputLength;
|
|
1151
|
+
this.parseAction = parseAction;
|
|
1152
|
+
this.errors = errors;
|
|
1153
|
+
this.offset = offset;
|
|
1154
|
+
this.rparensExpected = 0;
|
|
1155
|
+
this.rbracketsExpected = 0;
|
|
1156
|
+
this.rbracesExpected = 0;
|
|
1157
|
+
this.index = 0;
|
|
1158
|
+
}
|
|
1159
|
+
peek(offset) {
|
|
1160
|
+
const i = this.index + offset;
|
|
1161
|
+
return i < this.tokens.length ? this.tokens[i] : EOF;
|
|
1162
|
+
}
|
|
1163
|
+
get next() { return this.peek(0); }
|
|
1164
|
+
get inputIndex() {
|
|
1165
|
+
return (this.index < this.tokens.length) ? this.next.index + this.offset :
|
|
1166
|
+
this.inputLength + this.offset;
|
|
1167
|
+
}
|
|
1168
|
+
span(start) { return new ParseSpan(start, this.inputIndex); }
|
|
1169
|
+
advance() { this.index++; }
|
|
1170
|
+
optionalCharacter(code) {
|
|
1171
|
+
if (this.next.isCharacter(code)) {
|
|
1172
|
+
this.advance();
|
|
1173
|
+
return true;
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
peekKeywordLet() { return this.next.isKeywordLet(); }
|
|
1180
|
+
expectCharacter(code) {
|
|
1181
|
+
if (this.optionalCharacter(code))
|
|
1182
|
+
return;
|
|
1183
|
+
this.error(`Missing expected ${String.fromCharCode(code)}`);
|
|
1184
|
+
}
|
|
1185
|
+
optionalOperator(op) {
|
|
1186
|
+
if (this.next.isOperator(op)) {
|
|
1187
|
+
this.advance();
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
return false;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
expectOperator(operator) {
|
|
1195
|
+
if (this.optionalOperator(operator))
|
|
1196
|
+
return;
|
|
1197
|
+
this.error(`Missing expected operator ${operator}`);
|
|
1198
|
+
}
|
|
1199
|
+
expectIdentifierOrKeyword() {
|
|
1200
|
+
const n = this.next;
|
|
1201
|
+
if (!n.isIdentifier() && !n.isKeyword()) {
|
|
1202
|
+
this.error(`Unexpected token ${n}, expected identifier or keyword`);
|
|
1203
|
+
return '';
|
|
1204
|
+
}
|
|
1205
|
+
this.advance();
|
|
1206
|
+
return n.toString();
|
|
1207
|
+
}
|
|
1208
|
+
expectIdentifierOrKeywordOrString() {
|
|
1209
|
+
const n = this.next;
|
|
1210
|
+
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
|
1211
|
+
this.error(`Unexpected token ${n}, expected identifier, keyword, or string`);
|
|
1212
|
+
return '';
|
|
1213
|
+
}
|
|
1214
|
+
this.advance();
|
|
1215
|
+
return n.toString();
|
|
1216
|
+
}
|
|
1217
|
+
parseChain() {
|
|
1218
|
+
const exprs = [];
|
|
1219
|
+
const start = this.inputIndex;
|
|
1220
|
+
while (this.index < this.tokens.length) {
|
|
1221
|
+
const expr = this.parsePipe();
|
|
1222
|
+
exprs.push(expr);
|
|
1223
|
+
if (this.optionalCharacter($SEMICOLON)) {
|
|
1224
|
+
if (!this.parseAction) {
|
|
1225
|
+
this.error('Binding expression cannot contain chained expression');
|
|
1226
|
+
}
|
|
1227
|
+
while (this.optionalCharacter($SEMICOLON)) {
|
|
1228
|
+
} // read all semicolons
|
|
1229
|
+
}
|
|
1230
|
+
else if (this.index < this.tokens.length) {
|
|
1231
|
+
this.error(`Unexpected token '${this.next}'`);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
if (exprs.length == 0)
|
|
1235
|
+
return new EmptyExpr(this.span(start));
|
|
1236
|
+
if (exprs.length == 1)
|
|
1237
|
+
return exprs[0];
|
|
1238
|
+
return new Chain(this.span(start), exprs);
|
|
1239
|
+
}
|
|
1240
|
+
parsePipe() {
|
|
1241
|
+
let result = this.parseExpression();
|
|
1242
|
+
if (this.optionalOperator('|')) {
|
|
1243
|
+
if (this.parseAction) {
|
|
1244
|
+
this.error('Cannot have a pipe in an action expression');
|
|
1245
|
+
}
|
|
1246
|
+
do {
|
|
1247
|
+
const name = this.expectIdentifierOrKeyword();
|
|
1248
|
+
const args = [];
|
|
1249
|
+
while (this.optionalCharacter($COLON)) {
|
|
1250
|
+
args.push(this.parseExpression());
|
|
1251
|
+
}
|
|
1252
|
+
result = new BindingPipe(this.span(result.span.start), result, name, args);
|
|
1253
|
+
} while (this.optionalOperator('|'));
|
|
1254
|
+
}
|
|
1255
|
+
return result;
|
|
1256
|
+
}
|
|
1257
|
+
parseExpression() { return this.parseConditional(); }
|
|
1258
|
+
parseConditional() {
|
|
1259
|
+
const start = this.inputIndex;
|
|
1260
|
+
const result = this.parseLogicalOr();
|
|
1261
|
+
if (this.optionalOperator('?')) {
|
|
1262
|
+
const yes = this.parsePipe();
|
|
1263
|
+
let no;
|
|
1264
|
+
if (!this.optionalCharacter($COLON)) {
|
|
1265
|
+
const end = this.inputIndex;
|
|
1266
|
+
const expression = this.input.substring(start, end);
|
|
1267
|
+
this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
|
1268
|
+
no = new EmptyExpr(this.span(start));
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
no = this.parsePipe();
|
|
1272
|
+
}
|
|
1273
|
+
return new Conditional(this.span(start), result, yes, no);
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
return result;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
parseLogicalOr() {
|
|
1280
|
+
// '||'
|
|
1281
|
+
let result = this.parseLogicalAnd();
|
|
1282
|
+
while (this.optionalOperator('||')) {
|
|
1283
|
+
const right = this.parseLogicalAnd();
|
|
1284
|
+
result = new Binary(this.span(result.span.start), '||', result, right);
|
|
1285
|
+
}
|
|
1286
|
+
return result;
|
|
1287
|
+
}
|
|
1288
|
+
parseLogicalAnd() {
|
|
1289
|
+
// '&&'
|
|
1290
|
+
let result = this.parseEquality();
|
|
1291
|
+
while (this.optionalOperator('&&')) {
|
|
1292
|
+
const right = this.parseEquality();
|
|
1293
|
+
result = new Binary(this.span(result.span.start), '&&', result, right);
|
|
1294
|
+
}
|
|
1295
|
+
return result;
|
|
1296
|
+
}
|
|
1297
|
+
parseEquality() {
|
|
1298
|
+
// '==','!=','===','!=='
|
|
1299
|
+
let result = this.parseRelational();
|
|
1300
|
+
while (this.next.type == TokenType.Operator) {
|
|
1301
|
+
const operator = this.next.strValue;
|
|
1302
|
+
switch (operator) {
|
|
1303
|
+
case '==':
|
|
1304
|
+
case '===':
|
|
1305
|
+
case '!=':
|
|
1306
|
+
case '!==':
|
|
1307
|
+
this.advance();
|
|
1308
|
+
const right = this.parseRelational();
|
|
1309
|
+
result = new Binary(this.span(result.span.start), operator, result, right);
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
return result;
|
|
1315
|
+
}
|
|
1316
|
+
parseRelational() {
|
|
1317
|
+
// '<', '>', '<=', '>='
|
|
1318
|
+
let result = this.parseAdditive();
|
|
1319
|
+
while (this.next.type == TokenType.Operator) {
|
|
1320
|
+
const operator = this.next.strValue;
|
|
1321
|
+
switch (operator) {
|
|
1322
|
+
case '<':
|
|
1323
|
+
case '>':
|
|
1324
|
+
case '<=':
|
|
1325
|
+
case '>=':
|
|
1326
|
+
this.advance();
|
|
1327
|
+
const right = this.parseAdditive();
|
|
1328
|
+
result = new Binary(this.span(result.span.start), operator, result, right);
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
return result;
|
|
1334
|
+
}
|
|
1335
|
+
parseAdditive() {
|
|
1336
|
+
// '+', '-'
|
|
1337
|
+
let result = this.parseMultiplicative();
|
|
1338
|
+
while (this.next.type == TokenType.Operator) {
|
|
1339
|
+
const operator = this.next.strValue;
|
|
1340
|
+
switch (operator) {
|
|
1341
|
+
case '+':
|
|
1342
|
+
case '-':
|
|
1343
|
+
this.advance();
|
|
1344
|
+
let right = this.parseMultiplicative();
|
|
1345
|
+
result = new Binary(this.span(result.span.start), operator, result, right);
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
break;
|
|
1349
|
+
}
|
|
1350
|
+
return result;
|
|
1351
|
+
}
|
|
1352
|
+
parseMultiplicative() {
|
|
1353
|
+
// '*', '%', '/'
|
|
1354
|
+
let result = this.parsePrefix();
|
|
1355
|
+
while (this.next.type == TokenType.Operator) {
|
|
1356
|
+
const operator = this.next.strValue;
|
|
1357
|
+
switch (operator) {
|
|
1358
|
+
case '*':
|
|
1359
|
+
case '%':
|
|
1360
|
+
case '/':
|
|
1361
|
+
this.advance();
|
|
1362
|
+
let right = this.parsePrefix();
|
|
1363
|
+
result = new Binary(this.span(result.span.start), operator, result, right);
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
break;
|
|
1367
|
+
}
|
|
1368
|
+
return result;
|
|
1369
|
+
}
|
|
1370
|
+
parsePrefix() {
|
|
1371
|
+
if (this.next.type == TokenType.Operator) {
|
|
1372
|
+
const start = this.inputIndex;
|
|
1373
|
+
const operator = this.next.strValue;
|
|
1374
|
+
let result;
|
|
1375
|
+
switch (operator) {
|
|
1376
|
+
case '+':
|
|
1377
|
+
this.advance();
|
|
1378
|
+
return this.parsePrefix();
|
|
1379
|
+
case '-':
|
|
1380
|
+
this.advance();
|
|
1381
|
+
result = this.parsePrefix();
|
|
1382
|
+
return new Binary(this.span(start), operator, new LiteralPrimitive(new ParseSpan(start, start), 0), result);
|
|
1383
|
+
case '!':
|
|
1384
|
+
this.advance();
|
|
1385
|
+
result = this.parsePrefix();
|
|
1386
|
+
return new PrefixNot(this.span(start), result);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return this.parseCallChain();
|
|
1390
|
+
}
|
|
1391
|
+
parseCallChain() {
|
|
1392
|
+
let result = this.parsePrimary();
|
|
1393
|
+
while (true) {
|
|
1394
|
+
if (this.optionalCharacter($PERIOD)) {
|
|
1395
|
+
result = this.parseAccessMemberOrMethodCall(result, false);
|
|
1396
|
+
}
|
|
1397
|
+
else if (this.optionalOperator('?.')) {
|
|
1398
|
+
result = this.parseAccessMemberOrMethodCall(result, true);
|
|
1399
|
+
}
|
|
1400
|
+
else if (this.optionalCharacter($LBRACKET)) {
|
|
1401
|
+
this.rbracketsExpected++;
|
|
1402
|
+
const key = this.parsePipe();
|
|
1403
|
+
this.rbracketsExpected--;
|
|
1404
|
+
this.expectCharacter($RBRACKET);
|
|
1405
|
+
if (this.optionalOperator('=')) {
|
|
1406
|
+
const value = this.parseConditional();
|
|
1407
|
+
result = new KeyedWrite(this.span(result.span.start), result, key, value);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
result = new KeyedRead(this.span(result.span.start), result, key);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
else if (this.optionalCharacter($LPAREN)) {
|
|
1414
|
+
this.rparensExpected++;
|
|
1415
|
+
const args = this.parseCallArguments();
|
|
1416
|
+
this.rparensExpected--;
|
|
1417
|
+
this.expectCharacter($RPAREN);
|
|
1418
|
+
result = new FunctionCall(this.span(result.span.start), result, args);
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
return result;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
parsePrimary() {
|
|
1426
|
+
const start = this.inputIndex;
|
|
1427
|
+
if (this.optionalCharacter($LPAREN)) {
|
|
1428
|
+
this.rparensExpected++;
|
|
1429
|
+
const result = this.parsePipe();
|
|
1430
|
+
this.rparensExpected--;
|
|
1431
|
+
this.expectCharacter($RPAREN);
|
|
1432
|
+
return result;
|
|
1433
|
+
}
|
|
1434
|
+
else if (this.next.isKeywordNull()) {
|
|
1435
|
+
this.advance();
|
|
1436
|
+
return new LiteralPrimitive(this.span(start), null);
|
|
1437
|
+
}
|
|
1438
|
+
else if (this.next.isKeywordUndefined()) {
|
|
1439
|
+
this.advance();
|
|
1440
|
+
return new LiteralPrimitive(this.span(start), void 0);
|
|
1441
|
+
}
|
|
1442
|
+
else if (this.next.isKeywordTrue()) {
|
|
1443
|
+
this.advance();
|
|
1444
|
+
return new LiteralPrimitive(this.span(start), true);
|
|
1445
|
+
}
|
|
1446
|
+
else if (this.next.isKeywordFalse()) {
|
|
1447
|
+
this.advance();
|
|
1448
|
+
return new LiteralPrimitive(this.span(start), false);
|
|
1449
|
+
}
|
|
1450
|
+
else if (this.next.isKeywordThis()) {
|
|
1451
|
+
this.advance();
|
|
1452
|
+
return new ImplicitReceiver(this.span(start));
|
|
1453
|
+
}
|
|
1454
|
+
else if (this.optionalCharacter($LBRACKET)) {
|
|
1455
|
+
this.rbracketsExpected++;
|
|
1456
|
+
const elements = this.parseExpressionList($RBRACKET);
|
|
1457
|
+
this.rbracketsExpected--;
|
|
1458
|
+
this.expectCharacter($RBRACKET);
|
|
1459
|
+
return new LiteralArray(this.span(start), elements);
|
|
1460
|
+
}
|
|
1461
|
+
else if (this.next.isCharacter($LBRACE)) {
|
|
1462
|
+
return this.parseLiteralMap();
|
|
1463
|
+
}
|
|
1464
|
+
else if (this.next.isIdentifier()) {
|
|
1465
|
+
return this.parseAccessMemberOrMethodCall(new ImplicitReceiver(this.span(start)), false);
|
|
1466
|
+
}
|
|
1467
|
+
else if (this.next.isNumber()) {
|
|
1468
|
+
const value = this.next.toNumber();
|
|
1469
|
+
this.advance();
|
|
1470
|
+
return new LiteralPrimitive(this.span(start), value);
|
|
1471
|
+
}
|
|
1472
|
+
else if (this.next.isString()) {
|
|
1473
|
+
const literalValue = this.next.toString();
|
|
1474
|
+
this.advance();
|
|
1475
|
+
return new LiteralPrimitive(this.span(start), literalValue);
|
|
1476
|
+
}
|
|
1477
|
+
else if (this.index >= this.tokens.length) {
|
|
1478
|
+
this.error(`Unexpected end of expression: ${this.input}`);
|
|
1479
|
+
return new EmptyExpr(this.span(start));
|
|
1480
|
+
}
|
|
1481
|
+
else {
|
|
1482
|
+
this.error(`Unexpected token ${this.next}`);
|
|
1483
|
+
return new EmptyExpr(this.span(start));
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
parseExpressionList(terminator) {
|
|
1487
|
+
const result = [];
|
|
1488
|
+
if (!this.next.isCharacter(terminator)) {
|
|
1489
|
+
do {
|
|
1490
|
+
result.push(this.parsePipe());
|
|
1491
|
+
} while (this.optionalCharacter($COMMA));
|
|
1492
|
+
}
|
|
1493
|
+
return result;
|
|
1494
|
+
}
|
|
1495
|
+
parseLiteralMap() {
|
|
1496
|
+
const keys = [];
|
|
1497
|
+
const values = [];
|
|
1498
|
+
const start = this.inputIndex;
|
|
1499
|
+
this.expectCharacter($LBRACE);
|
|
1500
|
+
if (!this.optionalCharacter($RBRACE)) {
|
|
1501
|
+
this.rbracesExpected++;
|
|
1502
|
+
do {
|
|
1503
|
+
const key = this.expectIdentifierOrKeywordOrString();
|
|
1504
|
+
keys.push(key);
|
|
1505
|
+
this.expectCharacter($COLON);
|
|
1506
|
+
values.push(this.parsePipe());
|
|
1507
|
+
} while (this.optionalCharacter($COMMA));
|
|
1508
|
+
this.rbracesExpected--;
|
|
1509
|
+
this.expectCharacter($RBRACE);
|
|
1510
|
+
}
|
|
1511
|
+
return new LiteralMap(this.span(start), keys, values);
|
|
1512
|
+
}
|
|
1513
|
+
parseAccessMemberOrMethodCall(receiver, isSafe = false) {
|
|
1514
|
+
const start = receiver.span.start;
|
|
1515
|
+
const id = this.expectIdentifierOrKeyword();
|
|
1516
|
+
if (this.optionalCharacter($LPAREN)) {
|
|
1517
|
+
this.rparensExpected++;
|
|
1518
|
+
const args = this.parseCallArguments();
|
|
1519
|
+
this.expectCharacter($RPAREN);
|
|
1520
|
+
this.rparensExpected--;
|
|
1521
|
+
const span = this.span(start);
|
|
1522
|
+
return isSafe ? new SafeMethodCall(span, receiver, id, args) :
|
|
1523
|
+
new MethodCall(span, receiver, id, args);
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
if (isSafe) {
|
|
1527
|
+
if (this.optionalOperator('=')) {
|
|
1528
|
+
this.error('The \'?.\' operator cannot be used in the assignment');
|
|
1529
|
+
return new EmptyExpr(this.span(start));
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
return new SafePropertyRead(this.span(start), receiver, id);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
if (this.optionalOperator('=')) {
|
|
1537
|
+
if (!this.parseAction) {
|
|
1538
|
+
this.error('Bindings cannot contain assignments');
|
|
1539
|
+
return new EmptyExpr(this.span(start));
|
|
1540
|
+
}
|
|
1541
|
+
const value = this.parseConditional();
|
|
1542
|
+
return new PropertyWrite(this.span(start), receiver, id, value);
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
return new PropertyRead(this.span(start), receiver, id);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
parseCallArguments() {
|
|
1551
|
+
if (this.next.isCharacter($RPAREN))
|
|
1552
|
+
return [];
|
|
1553
|
+
const positionals = [];
|
|
1554
|
+
do {
|
|
1555
|
+
positionals.push(this.parsePipe());
|
|
1556
|
+
} while (this.optionalCharacter($COMMA));
|
|
1557
|
+
return positionals;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* An identifier, a keyword, a string with an optional `-` inbetween.
|
|
1561
|
+
*/
|
|
1562
|
+
expectTemplateBindingKey() {
|
|
1563
|
+
let result = '';
|
|
1564
|
+
let operatorFound = false;
|
|
1565
|
+
do {
|
|
1566
|
+
result += this.expectIdentifierOrKeywordOrString();
|
|
1567
|
+
operatorFound = this.optionalOperator('-');
|
|
1568
|
+
if (operatorFound) {
|
|
1569
|
+
result += '-';
|
|
1570
|
+
}
|
|
1571
|
+
} while (operatorFound);
|
|
1572
|
+
return result.toString();
|
|
1573
|
+
}
|
|
1574
|
+
parseTemplateBindings() {
|
|
1575
|
+
const bindings = [];
|
|
1576
|
+
let prefix = null;
|
|
1577
|
+
const warnings = [];
|
|
1578
|
+
while (this.index < this.tokens.length) {
|
|
1579
|
+
const start = this.inputIndex;
|
|
1580
|
+
const keyIsVar = this.peekKeywordLet();
|
|
1581
|
+
if (keyIsVar) {
|
|
1582
|
+
this.advance();
|
|
1583
|
+
}
|
|
1584
|
+
let key = this.expectTemplateBindingKey();
|
|
1585
|
+
if (!keyIsVar) {
|
|
1586
|
+
if (prefix == null) {
|
|
1587
|
+
prefix = key;
|
|
1588
|
+
}
|
|
1589
|
+
else {
|
|
1590
|
+
key = prefix + key[0].toUpperCase() + key.substring(1);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
this.optionalCharacter($COLON);
|
|
1594
|
+
let name = null;
|
|
1595
|
+
let expression = null;
|
|
1596
|
+
if (keyIsVar) {
|
|
1597
|
+
if (this.optionalOperator('=')) {
|
|
1598
|
+
name = this.expectTemplateBindingKey();
|
|
1599
|
+
}
|
|
1600
|
+
else {
|
|
1601
|
+
name = '\$implicit';
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
else if (this.next !== EOF && !this.peekKeywordLet()) {
|
|
1605
|
+
const start = this.inputIndex;
|
|
1606
|
+
const ast = this.parsePipe();
|
|
1607
|
+
const source = this.input.substring(start - this.offset, this.inputIndex - this.offset);
|
|
1608
|
+
expression = new ASTWithSource(ast, source, this.location, this.errors);
|
|
1609
|
+
}
|
|
1610
|
+
bindings.push(new TemplateBinding(this.span(start), key, keyIsVar, name, expression));
|
|
1611
|
+
if (!this.optionalCharacter($SEMICOLON)) {
|
|
1612
|
+
this.optionalCharacter($COMMA);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return new TemplateBindingParseResult(bindings, warnings, this.errors);
|
|
1616
|
+
}
|
|
1617
|
+
error(message, index = null) {
|
|
1618
|
+
this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
|
|
1619
|
+
this.skip();
|
|
1620
|
+
}
|
|
1621
|
+
locationText(index = null) {
|
|
1622
|
+
if (isBlank(index))
|
|
1623
|
+
index = this.index;
|
|
1624
|
+
return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
|
|
1625
|
+
`at the end of the expression`;
|
|
1626
|
+
}
|
|
1627
|
+
// Error recovery should skip tokens until it encounters a recovery point. skip() treats
|
|
1628
|
+
// the end of input and a ';' as unconditionally a recovery point. It also treats ')',
|
|
1629
|
+
// '}' and ']' as conditional recovery points if one of calling productions is expecting
|
|
1630
|
+
// one of these symbols. This allows skip() to recover from errors such as '(a.) + 1' allowing
|
|
1631
|
+
// more of the AST to be retained (it doesn't skip any tokens as the ')' is retained because
|
|
1632
|
+
// of the '(' begins an '(' <expr> ')' production). The recovery points of grouping symbols
|
|
1633
|
+
// must be conditional as they must be skipped if none of the calling productions are not
|
|
1634
|
+
// expecting the closing token else we will never make progress in the case of an
|
|
1635
|
+
// extraneous group closing symbol (such as a stray ')'). This is not the case for ';' because
|
|
1636
|
+
// parseChain() is always the root production and it expects a ';'.
|
|
1637
|
+
// If a production expects one of these token it increments the corresponding nesting count,
|
|
1638
|
+
// and then decrements it just prior to checking if the token is in the input.
|
|
1639
|
+
skip() {
|
|
1640
|
+
let n = this.next;
|
|
1641
|
+
while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
|
|
1642
|
+
(this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
|
|
1643
|
+
(this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
|
|
1644
|
+
(this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET))) {
|
|
1645
|
+
if (this.next.isError()) {
|
|
1646
|
+
this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
|
|
1647
|
+
}
|
|
1648
|
+
this.advance();
|
|
1649
|
+
n = this.next;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
class SimpleExpressionChecker {
|
|
1654
|
+
constructor() {
|
|
1655
|
+
this.errors = [];
|
|
1656
|
+
}
|
|
1657
|
+
static check(ast) {
|
|
1658
|
+
const s = new SimpleExpressionChecker();
|
|
1659
|
+
ast.visit(s);
|
|
1660
|
+
return s.errors;
|
|
1661
|
+
}
|
|
1662
|
+
visitImplicitReceiver(ast, context) { }
|
|
1663
|
+
visitInterpolation(ast, context) { }
|
|
1664
|
+
visitLiteralPrimitive(ast, context) { }
|
|
1665
|
+
visitPropertyRead(ast, context) { }
|
|
1666
|
+
visitPropertyWrite(ast, context) { }
|
|
1667
|
+
visitSafePropertyRead(ast, context) { }
|
|
1668
|
+
visitMethodCall(ast, context) { }
|
|
1669
|
+
visitSafeMethodCall(ast, context) { }
|
|
1670
|
+
visitFunctionCall(ast, context) { }
|
|
1671
|
+
visitLiteralArray(ast, context) { this.visitAll(ast.expressions); }
|
|
1672
|
+
visitLiteralMap(ast, context) { this.visitAll(ast.values); }
|
|
1673
|
+
visitBinary(ast, context) { }
|
|
1674
|
+
visitPrefixNot(ast, context) { }
|
|
1675
|
+
visitConditional(ast, context) { }
|
|
1676
|
+
visitPipe(ast, context) { this.errors.push('pipes'); }
|
|
1677
|
+
visitKeyedRead(ast, context) { }
|
|
1678
|
+
visitKeyedWrite(ast, context) { }
|
|
1679
|
+
visitAll(asts) { return asts.map(node => node.visit(this)); }
|
|
1680
|
+
visitChain(ast, context) { }
|
|
1681
|
+
visitQuote(ast, context) { }
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
const BinaryOperations = new Map([
|
|
1685
|
+
['==', (left, right) => left == right],
|
|
1686
|
+
['===', (left, right) => left === right],
|
|
1687
|
+
['!=', (left, right) => left != right],
|
|
1688
|
+
['!==', (left, right) => left !== right],
|
|
1689
|
+
['&&', (left, right) => left && right],
|
|
1690
|
+
['||', (left, right) => left || right],
|
|
1691
|
+
['+', (left, right) => left + right],
|
|
1692
|
+
['-', (left, right) => left - right],
|
|
1693
|
+
['/', (left, right) => left / right],
|
|
1694
|
+
['*', (left, right) => left * right],
|
|
1695
|
+
['%', (left, right) => left % right],
|
|
1696
|
+
['<', (left, right) => left < right],
|
|
1697
|
+
['<=', (left, right) => left <= right],
|
|
1698
|
+
['>', (left, right) => left > right],
|
|
1699
|
+
['>=', (left, right) => left >= right],
|
|
1700
|
+
]);
|
|
1701
|
+
|
|
1702
|
+
function compileToJSON(json) {
|
|
1703
|
+
return JSON.stringify(json).replace(/"/g, '');
|
|
1704
|
+
}
|
|
1705
|
+
function isPresent(obj) {
|
|
1706
|
+
return obj !== null && obj !== undefined;
|
|
1707
|
+
}
|
|
1708
|
+
function isJsObject(obj) {
|
|
1709
|
+
return obj !== null && (typeof obj === 'function' || typeof obj === 'object');
|
|
1710
|
+
}
|
|
1711
|
+
function isFunction(val) {
|
|
1712
|
+
return typeof val === 'function';
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
class ParseVisitorCompiler extends RecursiveAstVisitor {
|
|
1716
|
+
visitBinary(ast) {
|
|
1717
|
+
const left = ast.left.visit(this);
|
|
1718
|
+
const right = ast.right.visit(this);
|
|
1719
|
+
return `${left} ${ast.operation} ${right}`;
|
|
1720
|
+
}
|
|
1721
|
+
// TODO
|
|
1722
|
+
visitChain(ast) {
|
|
1723
|
+
return compileToJSON(this.visitAll(ast.expressions));
|
|
1724
|
+
}
|
|
1725
|
+
visitConditional(ast) {
|
|
1726
|
+
const condition = ast.condition.visit(this);
|
|
1727
|
+
const trueExp = ast.trueExp.visit(this);
|
|
1728
|
+
const falseExp = ast.falseExp.visit(this);
|
|
1729
|
+
return `${condition} ? ${trueExp} : ${falseExp}`;
|
|
1730
|
+
}
|
|
1731
|
+
visitPipe(ast) {
|
|
1732
|
+
const pipe = ast.name;
|
|
1733
|
+
const args = this.visitAll(ast.args);
|
|
1734
|
+
const value = ast.exp.visit(this);
|
|
1735
|
+
args.unshift(value);
|
|
1736
|
+
return `pipesCache.get('${pipe}').transform.apply(null, ${compileToJSON(args)})`;
|
|
1737
|
+
}
|
|
1738
|
+
// TODO
|
|
1739
|
+
visitFunctionCall(ast) {
|
|
1740
|
+
const target = ast.target.visit(this);
|
|
1741
|
+
const args = compileToJSON(this.visitAll(ast.args));
|
|
1742
|
+
return `${target}.apply(${target}, ${args})`;
|
|
1743
|
+
}
|
|
1744
|
+
visitImplicitReceiver(ast) {
|
|
1745
|
+
return `context`;
|
|
1746
|
+
}
|
|
1747
|
+
visitInterpolation(ast) {
|
|
1748
|
+
return this.visitAll(ast.expressions)[0];
|
|
1749
|
+
}
|
|
1750
|
+
visitKeyedRead(ast) {
|
|
1751
|
+
const obj = ast.obj.visit(this);
|
|
1752
|
+
const key = ast.key.visit(this);
|
|
1753
|
+
return `${obj}[${key}]`;
|
|
1754
|
+
}
|
|
1755
|
+
visitKeyedWrite(ast) {
|
|
1756
|
+
return null;
|
|
1757
|
+
}
|
|
1758
|
+
visitLiteralArray(ast) {
|
|
1759
|
+
return compileToJSON(this.visitAll(ast.expressions));
|
|
1760
|
+
}
|
|
1761
|
+
visitLiteralMap(ast) {
|
|
1762
|
+
const result = {};
|
|
1763
|
+
const keys = ast.keys;
|
|
1764
|
+
const values = this.visitAll(ast.values);
|
|
1765
|
+
for (let i = 0, length = keys.length; i < length; i++) {
|
|
1766
|
+
result[keys[i]] = values[i];
|
|
1767
|
+
}
|
|
1768
|
+
return compileToJSON(result);
|
|
1769
|
+
}
|
|
1770
|
+
visitLiteralPrimitive(ast) {
|
|
1771
|
+
return typeof ast.value === 'string' ? `'${ast.value}'` : ast.value;
|
|
1772
|
+
}
|
|
1773
|
+
visitMethodCall(ast) {
|
|
1774
|
+
const methodName = ast.name;
|
|
1775
|
+
const receiver = ast.receiver.visit(this);
|
|
1776
|
+
const args = compileToJSON(this.visitAll(ast.args));
|
|
1777
|
+
return `${receiver}['${methodName}'].apply(${receiver}, ${args})`;
|
|
1778
|
+
}
|
|
1779
|
+
visitPrefixNot(ast) {
|
|
1780
|
+
return ast.expression.visit(this);
|
|
1781
|
+
}
|
|
1782
|
+
visitPropertyRead(ast) {
|
|
1783
|
+
const property = ast.name;
|
|
1784
|
+
const receiver = ast.receiver.visit(this);
|
|
1785
|
+
return `${receiver}['${property}']`;
|
|
1786
|
+
}
|
|
1787
|
+
visitPropertyWrite(ast) {
|
|
1788
|
+
return null;
|
|
1789
|
+
}
|
|
1790
|
+
visitSafePropertyRead(ast) {
|
|
1791
|
+
const property = ast.name;
|
|
1792
|
+
const receiver = ast.receiver.visit(this);
|
|
1793
|
+
return `${receiver}['${property}']`;
|
|
1794
|
+
}
|
|
1795
|
+
visitSafeMethodCall(ast) {
|
|
1796
|
+
const methodName = ast.name;
|
|
1797
|
+
const receiver = ast.receiver.visit(this);
|
|
1798
|
+
const args = compileToJSON(this.visitAll(ast.args));
|
|
1799
|
+
return `${receiver}['${methodName}'].apply(${receiver}, ${args})`;
|
|
1800
|
+
}
|
|
1801
|
+
visitAll(asts) {
|
|
1802
|
+
return asts.map(ast => ast.visit(this));
|
|
1803
|
+
}
|
|
1804
|
+
visitQuote(ast) {
|
|
1805
|
+
return null;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
class ParseVisitorResolver extends RecursiveAstVisitor {
|
|
1810
|
+
constructor(pipes) {
|
|
1811
|
+
super();
|
|
1812
|
+
this.pipes = pipes;
|
|
1813
|
+
}
|
|
1814
|
+
;
|
|
1815
|
+
visitBinary(ast, context) {
|
|
1816
|
+
const execFn = BinaryOperations.get(ast.operation);
|
|
1817
|
+
if (!execFn) {
|
|
1818
|
+
throw new Error(`Parse ERROR: on visitBinary, unknown operator ${ast.operation}`);
|
|
1819
|
+
}
|
|
1820
|
+
return execFn(ast.left.visit(this, context), ast.right.visit(this, context));
|
|
1821
|
+
}
|
|
1822
|
+
// TODO
|
|
1823
|
+
visitChain(ast, context) {
|
|
1824
|
+
return this.visitAll(ast.expressions, context);
|
|
1825
|
+
}
|
|
1826
|
+
visitConditional(ast, context) {
|
|
1827
|
+
if (ast.condition.visit(this, context)) {
|
|
1828
|
+
return ast.trueExp.visit(this, context);
|
|
1829
|
+
}
|
|
1830
|
+
else if (isPresent(ast.falseExp)) {
|
|
1831
|
+
return ast.falseExp.visit(this, context);
|
|
1832
|
+
}
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
visitPipe(ast, context) {
|
|
1836
|
+
const pipe = this.pipes.get(ast.name);
|
|
1837
|
+
if (!pipe) {
|
|
1838
|
+
throw new Error(`pipe ${ast.name} not found.`);
|
|
1839
|
+
}
|
|
1840
|
+
if (!pipe.transform) {
|
|
1841
|
+
throw new Error(`Parse ERROR: on visitPipe, transform method doesn't exist on pipe ${ast.name}.`);
|
|
1842
|
+
}
|
|
1843
|
+
const value = ast.exp.visit(this, context);
|
|
1844
|
+
const pipeArgs = this.visitAll(ast.args, context);
|
|
1845
|
+
pipeArgs.unshift(value);
|
|
1846
|
+
return pipe.transform.apply(null, pipeArgs);
|
|
1847
|
+
}
|
|
1848
|
+
// TODO
|
|
1849
|
+
visitFunctionCall(ast, context) {
|
|
1850
|
+
const target = ast.target.visit(this, context);
|
|
1851
|
+
if (!isFunction(target)) {
|
|
1852
|
+
throw new Error(`Parse ERROR: on visitFunctionCall, target is not a function.`);
|
|
1853
|
+
}
|
|
1854
|
+
const args = this.visitAll(ast.args, context);
|
|
1855
|
+
return target.apply(target, args);
|
|
1856
|
+
}
|
|
1857
|
+
visitImplicitReceiver(ast, context) {
|
|
1858
|
+
return context;
|
|
1859
|
+
}
|
|
1860
|
+
visitInterpolation(ast, context) {
|
|
1861
|
+
return this.visitAll(ast.expressions, context)[0];
|
|
1862
|
+
}
|
|
1863
|
+
visitKeyedRead(ast, context) {
|
|
1864
|
+
const obj = ast.obj.visit(this, context);
|
|
1865
|
+
const key = ast.key.visit(this, context);
|
|
1866
|
+
return obj[key];
|
|
1867
|
+
}
|
|
1868
|
+
visitKeyedWrite(ast, context) {
|
|
1869
|
+
const obj = ast.obj.visit(this, context);
|
|
1870
|
+
const key = ast.key.visit(this, context);
|
|
1871
|
+
const value = ast.value.visit(this, context);
|
|
1872
|
+
obj[key] = value;
|
|
1873
|
+
return null;
|
|
1874
|
+
}
|
|
1875
|
+
visitLiteralArray(ast, context) {
|
|
1876
|
+
return this.visitAll(ast.expressions, context);
|
|
1877
|
+
}
|
|
1878
|
+
visitLiteralMap(ast, context) {
|
|
1879
|
+
const result = {};
|
|
1880
|
+
const keys = ast.keys;
|
|
1881
|
+
const values = this.visitAll(ast.values, context);
|
|
1882
|
+
for (let i = 0, length = keys.length; i < length; i++) {
|
|
1883
|
+
result[keys[i]] = values[i];
|
|
1884
|
+
}
|
|
1885
|
+
return result;
|
|
1886
|
+
}
|
|
1887
|
+
visitLiteralPrimitive(ast, context) {
|
|
1888
|
+
return ast.value;
|
|
1889
|
+
}
|
|
1890
|
+
visitMethodCall(ast, context) {
|
|
1891
|
+
const receiver = ast.receiver.visit(this, context);
|
|
1892
|
+
if (!isJsObject(receiver)) {
|
|
1893
|
+
throw new Error(`Parse ERROR: on visitMethodCall, invalid method receiver.`);
|
|
1894
|
+
}
|
|
1895
|
+
const method = receiver[ast.name];
|
|
1896
|
+
if (!isFunction(method)) {
|
|
1897
|
+
throw new Error(`Parse ERROR: on visitMethodCall, method ${ast.name} doesn't exist on receiver.`);
|
|
1898
|
+
}
|
|
1899
|
+
const args = this.visitAll(ast.args, context);
|
|
1900
|
+
return method.apply(receiver, args);
|
|
1901
|
+
}
|
|
1902
|
+
visitPrefixNot(ast, context) {
|
|
1903
|
+
return ast.expression.visit(this, context);
|
|
1904
|
+
}
|
|
1905
|
+
visitPropertyRead(ast, context) {
|
|
1906
|
+
const receiver = ast.receiver.visit(this, context);
|
|
1907
|
+
if (!isJsObject(receiver)) {
|
|
1908
|
+
throw new Error(`Parse ERROR: on visitPropertyRead, invalid property receiver.`);
|
|
1909
|
+
}
|
|
1910
|
+
return receiver[ast.name];
|
|
1911
|
+
}
|
|
1912
|
+
visitPropertyWrite(ast, context) {
|
|
1913
|
+
const receiver = ast.receiver.visit(this, context);
|
|
1914
|
+
if (!isJsObject(receiver)) {
|
|
1915
|
+
throw new Error(`Parse ERROR: on visitPropertyRead, invalid property receiver.`);
|
|
1916
|
+
}
|
|
1917
|
+
receiver[ast.name] = ast.value.visit(this, context);
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
visitSafePropertyRead(ast, context) {
|
|
1921
|
+
const receiver = ast.receiver.visit(this, context);
|
|
1922
|
+
if (!isJsObject(receiver)) {
|
|
1923
|
+
throw new Error(`Parse ERROR: on visitSafePropertyRead, invalid property receiver.`);
|
|
1924
|
+
}
|
|
1925
|
+
return receiver[ast.name];
|
|
1926
|
+
}
|
|
1927
|
+
visitSafeMethodCall(ast, context) {
|
|
1928
|
+
const receiver = ast.receiver.visit(this, context);
|
|
1929
|
+
if (!isJsObject(receiver)) {
|
|
1930
|
+
throw new Error(`Parse ERROR: on visitSafeMethodCall, invalid method receiver.`);
|
|
1931
|
+
}
|
|
1932
|
+
const method = receiver[ast.name];
|
|
1933
|
+
if (!isFunction(method)) {
|
|
1934
|
+
throw new Error(`Parse ERROR: on visitSafeMethodCall, method ${ast.name} doesn't exist on receiver.`);
|
|
1935
|
+
}
|
|
1936
|
+
const args = this.visitAll(ast.args, context);
|
|
1937
|
+
return method.apply(receiver, args);
|
|
1938
|
+
}
|
|
1939
|
+
visitAll(asts, context) {
|
|
1940
|
+
return asts.map(ast => ast.visit(this, context));
|
|
1941
|
+
}
|
|
1942
|
+
visitQuote(ast, context) {
|
|
1943
|
+
throw new Error(`Parse ERROR: on visitQuote, quote expression not allowed.`);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
const PIPES_CONFIG = new InjectionToken('PipesConfig');
|
|
1948
|
+
class Parse {
|
|
1949
|
+
/**
|
|
1950
|
+
* Used to dependency inject the Angular 2 parser.
|
|
1951
|
+
*/
|
|
1952
|
+
constructor(pipesConfigs) {
|
|
1953
|
+
this._parser = new Parser(new Lexer());
|
|
1954
|
+
this._pipesCache = new Map();
|
|
1955
|
+
this._evalCache = new Map();
|
|
1956
|
+
this._calcCache = new Map();
|
|
1957
|
+
if (pipesConfigs && pipesConfigs.length) {
|
|
1958
|
+
pipesConfigs
|
|
1959
|
+
.filter(pipes => pipes && pipes.length)
|
|
1960
|
+
.forEach(pipes => pipes.forEach((pipeData) => this._pipesCache.set(pipeData.pipeName, pipeData.pipeInstance)));
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
eval(expression) {
|
|
1964
|
+
if (this._evalCache.has(expression)) {
|
|
1965
|
+
return this._evalCache.get(expression);
|
|
1966
|
+
}
|
|
1967
|
+
const visitor = new ParseVisitorCompiler();
|
|
1968
|
+
let ast = this._parser.parseInterpolation(expression, 'Parse');
|
|
1969
|
+
if (!ast) {
|
|
1970
|
+
ast = this._parser.parseBinding(expression, 'Parse');
|
|
1971
|
+
}
|
|
1972
|
+
const fnBody = ast.visit(visitor);
|
|
1973
|
+
const pipesCache = this._pipesCache;
|
|
1974
|
+
const getFn = new Function('context', 'pipesCache', `return ${fnBody};`);
|
|
1975
|
+
const evalParse = function evalParse(context) {
|
|
1976
|
+
return getFn(context, pipesCache);
|
|
1977
|
+
};
|
|
1978
|
+
this._evalCache.set(expression, evalParse);
|
|
1979
|
+
return evalParse;
|
|
1980
|
+
}
|
|
1981
|
+
calc(expression) {
|
|
1982
|
+
if (this._calcCache.has(expression)) {
|
|
1983
|
+
return this._calcCache.get(expression);
|
|
1984
|
+
}
|
|
1985
|
+
const visitor = new ParseVisitorResolver(this._pipesCache);
|
|
1986
|
+
let ast = this._parser.parseInterpolation(expression, 'Parse');
|
|
1987
|
+
if (!ast) {
|
|
1988
|
+
ast = this._parser.parseBinding(expression, 'Parse');
|
|
1989
|
+
}
|
|
1990
|
+
const calcParse = function calcParse(context) {
|
|
1991
|
+
return ast.visit(visitor, context);
|
|
1992
|
+
};
|
|
1993
|
+
this._calcCache.set(expression, calcParse);
|
|
1994
|
+
return calcParse;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
Parse.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Parse, deps: [{ token: PIPES_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1998
|
+
Parse.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Parse, providedIn: 'root' });
|
|
1999
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Parse, decorators: [{
|
|
2000
|
+
type: Injectable,
|
|
2001
|
+
args: [{ providedIn: 'root' }]
|
|
2002
|
+
}], ctorParameters: function () {
|
|
2003
|
+
return [{ type: undefined, decorators: [{
|
|
2004
|
+
type: Optional
|
|
2005
|
+
}, {
|
|
2006
|
+
type: Inject,
|
|
2007
|
+
args: [PIPES_CONFIG]
|
|
2008
|
+
}] }];
|
|
2009
|
+
} });
|
|
2010
|
+
|
|
2011
|
+
class Angular2ParseModule {
|
|
2012
|
+
static forRoot(pipesConfigMap) {
|
|
2013
|
+
return {
|
|
2014
|
+
ngModule: Angular2ParseModule,
|
|
2015
|
+
providers: [{ provide: PIPES_CONFIG, multi: true, useValue: pipesConfigMap || [] }]
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
Angular2ParseModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Angular2ParseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2020
|
+
Angular2ParseModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Angular2ParseModule });
|
|
2021
|
+
Angular2ParseModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Angular2ParseModule });
|
|
2022
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: Angular2ParseModule, decorators: [{
|
|
2023
|
+
type: NgModule
|
|
2024
|
+
}] });
|
|
2025
|
+
|
|
2026
|
+
/*
|
|
2027
|
+
* Public API Surface of angular2parse
|
|
2028
|
+
*/
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Generated bundle index. Do not edit.
|
|
2032
|
+
*/
|
|
2033
|
+
|
|
2034
|
+
export { Angular2ParseModule, PIPES_CONFIG, Parse };
|
|
2035
|
+
//# sourceMappingURL=auscope-angular2parse.mjs.map
|