@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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { $parseMinErr } from "./parse";
|
|
2
|
+
|
|
3
|
+
import { isDefined } from "../../shared/utils";
|
|
4
|
+
|
|
5
|
+
const ESCAPE = {
|
|
6
|
+
n: "\n",
|
|
7
|
+
f: "\f",
|
|
8
|
+
r: "\r",
|
|
9
|
+
t: "\t",
|
|
10
|
+
v: "\v",
|
|
11
|
+
"'": "'",
|
|
12
|
+
'"': '"',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const OPERATORS = new Set(
|
|
16
|
+
"+ - * / % === !== == != < > <= >= && || ! = |".split(" "),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} LexerOptions
|
|
21
|
+
* @property {(ch: string, codePoint: number) => boolean} [isIdentifierStart] Custom function to determine if a character is a valid identifier start.
|
|
22
|
+
* @property {(ch: string, codePoint: number) => boolean} [isIdentifierContinue] Custom function to determine if a character is a valid identifier continuation.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Represents a token produced by the lexer.
|
|
27
|
+
* @typedef {Object} Token
|
|
28
|
+
* @property {number} index Index of the token.
|
|
29
|
+
* @property {string} text Text of the token.
|
|
30
|
+
* @property {boolean} [identifier] Indicates if token is an identifier.
|
|
31
|
+
* @property {boolean} [constant] Indicates if token is a constant.
|
|
32
|
+
* @property {string|number} [value] Value of the token if it's a constant.
|
|
33
|
+
* @property {boolean} [operator] Indicates if token is an operator.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a lexer that tokenizes input text. The Lexer takes the original expression string and returns an array of tokens parsed from that string.
|
|
38
|
+
* For example, the string "a + b" would result in tokens for a, +, and b.
|
|
39
|
+
*/
|
|
40
|
+
export class Lexer {
|
|
41
|
+
/**
|
|
42
|
+
* Creates an instance of Lexer.
|
|
43
|
+
* @param {LexerOptions} options Lexer options.
|
|
44
|
+
*/
|
|
45
|
+
constructor(options) {
|
|
46
|
+
/** @type {LexerOptions} */
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Tokenizes the input text.
|
|
52
|
+
* @param {string} text Input text to lex.
|
|
53
|
+
* @returns {Array<Token>} Array of tokens.
|
|
54
|
+
*/
|
|
55
|
+
lex(text) {
|
|
56
|
+
this.text = text;
|
|
57
|
+
this.index = 0;
|
|
58
|
+
/** @type {Array<Token>} */
|
|
59
|
+
this.tokens = [];
|
|
60
|
+
|
|
61
|
+
while (this.index < this.text.length) {
|
|
62
|
+
const ch = this.text.charAt(this.index);
|
|
63
|
+
if (ch === '"' || ch === "'") {
|
|
64
|
+
this.readString(ch);
|
|
65
|
+
} else if (
|
|
66
|
+
this.isNumber(ch) ||
|
|
67
|
+
(ch === "." && this.isNumber(/** @type {string} */ (this.peek())))
|
|
68
|
+
) {
|
|
69
|
+
this.readNumber();
|
|
70
|
+
} else if (this.isIdentifierStart(this.peekMultichar())) {
|
|
71
|
+
this.readIdent();
|
|
72
|
+
} else if (this.is(ch, "(){}[].,;:?")) {
|
|
73
|
+
this.tokens.push({ index: this.index, text: ch });
|
|
74
|
+
this.index++;
|
|
75
|
+
} else if (this.isWhitespace(ch)) {
|
|
76
|
+
this.index++;
|
|
77
|
+
} else {
|
|
78
|
+
const ch2 = ch + this.peek();
|
|
79
|
+
const ch3 = ch2 + this.peek(2);
|
|
80
|
+
const op1 = OPERATORS.has(ch);
|
|
81
|
+
const op2 = OPERATORS.has(ch2);
|
|
82
|
+
const op3 = OPERATORS.has(ch3);
|
|
83
|
+
if (op1 || op2 || op3) {
|
|
84
|
+
const token = op3 ? ch3 : op2 ? ch2 : ch;
|
|
85
|
+
this.tokens.push({ index: this.index, text: token, operator: true });
|
|
86
|
+
this.index += token.length;
|
|
87
|
+
} else {
|
|
88
|
+
this.throwError(
|
|
89
|
+
"Unexpected next character ",
|
|
90
|
+
this.index,
|
|
91
|
+
this.index + 1,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return this.tokens;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Checks if a character is contained in a set of characters.
|
|
101
|
+
* @param {string} ch Character to check.
|
|
102
|
+
* @param {string} chars Set of characters.
|
|
103
|
+
* @returns {boolean} True if character is in the set, false otherwise.
|
|
104
|
+
*/
|
|
105
|
+
is(ch, chars) {
|
|
106
|
+
return chars.indexOf(ch) !== -1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Peeks at the next character in the text.
|
|
111
|
+
* @param {number} [i=1] Number of characters to peek.
|
|
112
|
+
* @returns {string|false} Next character or false if end of text.
|
|
113
|
+
*/
|
|
114
|
+
peek(i) {
|
|
115
|
+
const num = i || 1;
|
|
116
|
+
return this.index + num < this.text.length
|
|
117
|
+
? this.text.charAt(this.index + num)
|
|
118
|
+
: false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Checks if a character is a number.
|
|
123
|
+
* @param {string} ch Character to check.
|
|
124
|
+
* @returns {boolean} True if character is a number, false otherwise.
|
|
125
|
+
*/
|
|
126
|
+
isNumber(ch) {
|
|
127
|
+
return ch >= "0" && ch <= "9" && typeof ch === "string";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Checks if a character is whitespace.
|
|
132
|
+
* @param {string} ch Character to check.
|
|
133
|
+
* @returns {boolean} True if character is whitespace, false otherwise.
|
|
134
|
+
*/
|
|
135
|
+
isWhitespace(ch) {
|
|
136
|
+
// IE treats non-breaking space as \u00A0
|
|
137
|
+
return (
|
|
138
|
+
ch === " " ||
|
|
139
|
+
ch === "\r" ||
|
|
140
|
+
ch === "\t" ||
|
|
141
|
+
ch === "\n" ||
|
|
142
|
+
ch === "\v" ||
|
|
143
|
+
ch === "\u00A0"
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Checks if a character is a valid identifier start.
|
|
149
|
+
* @param {string} ch Character to check.
|
|
150
|
+
* @returns {boolean} True if character is a valid identifier start, false otherwise.
|
|
151
|
+
*/
|
|
152
|
+
isIdentifierStart(ch) {
|
|
153
|
+
return this.options.isIdentifierStart
|
|
154
|
+
? this.options.isIdentifierStart(ch, this.codePointAt(ch))
|
|
155
|
+
: (ch >= "a" && ch <= "z") ||
|
|
156
|
+
(ch >= "A" && ch <= "Z") ||
|
|
157
|
+
ch === "_" ||
|
|
158
|
+
ch === "$";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Checks if a character is a valid identifier continuation.
|
|
163
|
+
* @param {string} ch Character to check.
|
|
164
|
+
* @returns {boolean} True if character is a valid identifier continuation, false otherwise.
|
|
165
|
+
*/
|
|
166
|
+
isIdentifierContinue(ch) {
|
|
167
|
+
return this.options.isIdentifierContinue
|
|
168
|
+
? this.options.isIdentifierContinue(ch, this.codePointAt(ch))
|
|
169
|
+
: (ch >= "a" && ch <= "z") ||
|
|
170
|
+
(ch >= "A" && ch <= "Z") ||
|
|
171
|
+
ch === "_" ||
|
|
172
|
+
ch === "$" ||
|
|
173
|
+
(ch >= "0" && ch <= "9");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Converts a character to its Unicode code point.
|
|
178
|
+
* @param {string} ch Character to convert.
|
|
179
|
+
* @returns {number} Unicode code point.
|
|
180
|
+
*/
|
|
181
|
+
codePointAt(ch) {
|
|
182
|
+
if (ch.length === 1) return ch.charCodeAt(0);
|
|
183
|
+
|
|
184
|
+
return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Peeks at the next multicharacter sequence in the text.
|
|
189
|
+
* @returns {string} Next multicharacter sequence.
|
|
190
|
+
*/
|
|
191
|
+
peekMultichar() {
|
|
192
|
+
const ch = this.text.charAt(this.index);
|
|
193
|
+
const peek = this.peek();
|
|
194
|
+
if (!peek) {
|
|
195
|
+
return ch;
|
|
196
|
+
}
|
|
197
|
+
const cp1 = ch.charCodeAt(0);
|
|
198
|
+
const cp2 = peek.charCodeAt(0);
|
|
199
|
+
if (cp1 >= 0xd800 && cp1 <= 0xdbff && cp2 >= 0xdc00 && cp2 <= 0xdfff) {
|
|
200
|
+
return ch + peek;
|
|
201
|
+
}
|
|
202
|
+
return ch;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Checks if a character is an exponent operator.
|
|
207
|
+
* @param {string} ch Character to check.
|
|
208
|
+
* @returns {boolean} True if character is an exponent operator, false otherwise.
|
|
209
|
+
*/
|
|
210
|
+
isExpOperator(ch) {
|
|
211
|
+
return ch === "-" || ch === "+" || this.isNumber(ch);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Throws a lexer error.
|
|
216
|
+
* @param {string} error Error message.
|
|
217
|
+
* @param {number} [start] Start index.
|
|
218
|
+
* @param {number} [end] End index.
|
|
219
|
+
* @throws {Error} Lexer error.
|
|
220
|
+
*/
|
|
221
|
+
throwError(error, start, end) {
|
|
222
|
+
end = end || this.index;
|
|
223
|
+
const colStr = isDefined(start)
|
|
224
|
+
? `s ${start}-${this.index} [${this.text.substring(start, end)}]`
|
|
225
|
+
: ` ${end}`;
|
|
226
|
+
throw $parseMinErr(
|
|
227
|
+
"lexerr",
|
|
228
|
+
`Lexer Error: ${error} at column${colStr} in expression [${this.text}].`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Reads and tokenizes a number from the text.
|
|
234
|
+
*/
|
|
235
|
+
readNumber() {
|
|
236
|
+
let number = "";
|
|
237
|
+
const start = this.index;
|
|
238
|
+
while (this.index < this.text.length) {
|
|
239
|
+
const ch = this.text.charAt(this.index).toLowerCase();
|
|
240
|
+
if (ch === "." || this.isNumber(ch)) {
|
|
241
|
+
number += ch;
|
|
242
|
+
} else {
|
|
243
|
+
const peekCh = this.peek();
|
|
244
|
+
if (ch === "e" && this.isExpOperator(/** @type {string} */ (peekCh))) {
|
|
245
|
+
number += ch;
|
|
246
|
+
} else if (
|
|
247
|
+
this.isExpOperator(ch) &&
|
|
248
|
+
peekCh &&
|
|
249
|
+
this.isNumber(peekCh) &&
|
|
250
|
+
number.charAt(number.length - 1) === "e"
|
|
251
|
+
) {
|
|
252
|
+
number += ch;
|
|
253
|
+
} else if (
|
|
254
|
+
this.isExpOperator(ch) &&
|
|
255
|
+
(!peekCh || !this.isNumber(peekCh)) &&
|
|
256
|
+
number.charAt(number.length - 1) === "e"
|
|
257
|
+
) {
|
|
258
|
+
this.throwError("Invalid exponent");
|
|
259
|
+
} else {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.index++;
|
|
264
|
+
}
|
|
265
|
+
this.tokens.push({
|
|
266
|
+
index: start,
|
|
267
|
+
text: number,
|
|
268
|
+
constant: true,
|
|
269
|
+
value: Number(number),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Reads and tokenizes an identifier from the text.
|
|
275
|
+
*/
|
|
276
|
+
readIdent() {
|
|
277
|
+
const start = this.index;
|
|
278
|
+
this.index += this.peekMultichar().length;
|
|
279
|
+
while (this.index < this.text.length) {
|
|
280
|
+
const ch = this.peekMultichar();
|
|
281
|
+
if (!this.isIdentifierContinue(ch)) {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
this.index += ch.length;
|
|
285
|
+
}
|
|
286
|
+
this.tokens.push({
|
|
287
|
+
index: start,
|
|
288
|
+
text: this.text.slice(start, this.index),
|
|
289
|
+
identifier: true,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Reads and tokenizes a string from the text.
|
|
295
|
+
* @param {string} quote Quote character used for the string.
|
|
296
|
+
*/
|
|
297
|
+
readString(quote) {
|
|
298
|
+
const start = this.index;
|
|
299
|
+
let string = "";
|
|
300
|
+
let escape = false;
|
|
301
|
+
|
|
302
|
+
this.index++; // Skip opening quote
|
|
303
|
+
|
|
304
|
+
while (this.index < this.text.length) {
|
|
305
|
+
const ch = this.text[this.index];
|
|
306
|
+
|
|
307
|
+
if (escape) {
|
|
308
|
+
if (ch === "u") {
|
|
309
|
+
// Handle unicode escapes
|
|
310
|
+
// Simplified for brevity
|
|
311
|
+
string += this.handleUnicodeEscape();
|
|
312
|
+
} else {
|
|
313
|
+
string += ESCAPE[ch] || ch;
|
|
314
|
+
}
|
|
315
|
+
escape = false;
|
|
316
|
+
} else if (ch === "\\") {
|
|
317
|
+
escape = true;
|
|
318
|
+
} else if (ch === quote) {
|
|
319
|
+
this.tokens.push({
|
|
320
|
+
index: start,
|
|
321
|
+
text: this.text.slice(start, this.index + 1),
|
|
322
|
+
constant: true,
|
|
323
|
+
value: string,
|
|
324
|
+
});
|
|
325
|
+
this.index++; // Skip closing quote
|
|
326
|
+
return;
|
|
327
|
+
} else {
|
|
328
|
+
string += ch;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this.index++;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.throwError("Unterminated quote", start);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
handleUnicodeEscape() {
|
|
338
|
+
const hex = this.text.substring(this.index + 1, this.index + 5);
|
|
339
|
+
if (!hex.match(/[\da-f]{4}/i)) {
|
|
340
|
+
this.throwError(`Invalid unicode escape [\\u${hex}]`);
|
|
341
|
+
}
|
|
342
|
+
this.index += 4; // Move index past the four hexadecimal digits
|
|
343
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
344
|
+
}
|
|
345
|
+
}
|