@angular-wave/angular.ts 0.0.48 → 0.0.50

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.
Files changed (38) hide show
  1. package/Makefile +3 -0
  2. package/README.md +1 -1
  3. package/css/angular.css +0 -6
  4. package/dist/angular-ts.esm.js +2 -2
  5. package/dist/angular-ts.umd.js +2 -2
  6. package/jsdoc.json +24 -0
  7. package/package.json +6 -2
  8. package/src/core/compile/compile.md +1 -1
  9. package/src/core/compile/compile.spec.js +50 -48
  10. package/src/core/interpolate/interpolate.js +1 -18
  11. package/src/core/on.spec.js +7 -12
  12. package/src/core/parser/ast.js +234 -69
  13. package/src/core/parser/interpreter.js +246 -50
  14. package/src/core/parser/lexer.js +144 -57
  15. package/src/core/parser/parse.js +38 -278
  16. package/src/core/parser/parse.md +44 -0
  17. package/src/core/parser/parser.js +32 -6
  18. package/src/core/parser/shared.js +7 -1
  19. package/src/core/prop.spec.js +4 -4
  20. package/src/core/scope/scope.js +14 -10
  21. package/src/directive/csp.md +0 -26
  22. package/src/directive/form/form.spec.js +18 -18
  23. package/src/directive/include/include.spec.js +18 -18
  24. package/src/directive/switch/switch.spec.js +4 -4
  25. package/src/shared/constants.js +3 -2
  26. package/src/shared/jqlite/jqlite.js +2 -2
  27. package/src/types.js +4 -1
  28. package/types/core/parser/ast.d.ts +269 -67
  29. package/types/core/parser/interpreter.d.ts +202 -53
  30. package/types/core/parser/lexer.d.ts +153 -0
  31. package/types/core/parser/parse.d.ts +23 -34
  32. package/types/core/parser/parser.d.ts +43 -14
  33. package/types/core/parser/shared.d.ts +7 -1
  34. package/types/core/scope/scope.d.ts +11 -11
  35. package/types/shared/jqlite/jqlite.d.ts +4 -4
  36. package/types/types.d.ts +1 -1
  37. package/src/core/parser/compiler.js +0 -561
  38. package/types/core/parser/compiler.d.ts +0 -49
@@ -2,12 +2,55 @@ import { $parseMinErr } from "./parse";
2
2
  import { isAssignable } from "./shared";
3
3
  import { ASTType } from "./ast-type";
4
4
 
5
- export function AST(lexer, options) {
6
- this.lexer = lexer;
7
- this.options = options;
8
- }
5
+ /**
6
+ * @typedef {Object} ASTNode
7
+ * @property {string} type - The type of the AST node.
8
+ * @property {string} [name] - The name of the identifier.
9
+ * @property {string} [kind] - The kind of the property (e.g., 'init').
10
+ * @property {*} [value] - The value of the node if it is a literal.
11
+ * @property {ASTNode[]} [elements] - The elements of an array node.
12
+ * @property {ASTNode[]} [properties] - The properties of an object node.
13
+ * @property {ASTNode} [key] - The key of an object property.
14
+ * @property {ASTNode} [value] - The value of an object property.
15
+ * @property {ASTNode} [left] - The left-hand side of a binary expression.
16
+ * @property {ASTNode} [right] - The right-hand side of a binary expression.
17
+ * @property {ASTNode} [argument] - The argument of a unary expression.
18
+ * @property {ASTNode} [test] - The test expression of a conditional expression.
19
+ * @property {ASTNode} [alternate] - The alternate expression of a conditional expression.
20
+ * @property {ASTNode} [consequent] - The consequent expression of a conditional expression.
21
+ * @property {ASTNode[]} [body] - The body of a program or block statement.
22
+ * @property {ASTNode} [expression] - The expression of an expression statement.
23
+ * @property {ASTNode} [callee] - The callee of a call expression.
24
+ * @property {ASTNode[]} [arguments] - The arguments of a call expression.
25
+ * @property {boolean} [prefix] - Indicates if a unary operator is a prefix.
26
+ * @property {ASTNode} [object] - The object of a member expression.
27
+ * @property {ASTNode} [property] - The property of a member expression.
28
+ * @property {boolean} [computed] - Indicates if a member expression is computed.
29
+ * @property {string} [operator] - The operator of a binary or logical expression.
30
+ */
31
+
32
+ /**
33
+ * @param {import('./lexer').Lexer} lexer - The lexer instance for tokenizing input
34
+ * @param {import("./parser").ParserOptions} options
35
+ */
36
+ export class AST {
37
+ constructor(lexer, options) {
38
+ /** @type {import('./lexer').Lexer} */
39
+ this.lexer = lexer;
40
+
41
+ /** @type {import("./parser").ParserOptions} */
42
+ this.options = options;
43
+ this.selfReferential = {
44
+ this: { type: ASTType.ThisExpression },
45
+ $locals: { type: ASTType.LocalsExpression },
46
+ };
47
+ }
9
48
 
10
- AST.prototype = {
49
+ /**
50
+ * Parses the input text and generates an AST.
51
+ * @param {string} text - The input text to parse.
52
+ * @returns {ASTNode} The root node of the AST.
53
+ */
11
54
  ast(text) {
12
55
  this.text = text;
13
56
  this.tokens = this.lexer.lex(text);
@@ -19,8 +62,12 @@ AST.prototype = {
19
62
  }
20
63
 
21
64
  return value;
22
- },
65
+ }
23
66
 
67
+ /**
68
+ * Parses a program.
69
+ * @returns {ASTNode} The program node.
70
+ */
24
71
  program() {
25
72
  const body = [];
26
73
  let hasMore = true;
@@ -32,27 +79,43 @@ AST.prototype = {
32
79
  }
33
80
  }
34
81
  return { type: ASTType.Program, body };
35
- },
82
+ }
36
83
 
84
+ /**
85
+ * Parses an expression statement.
86
+ * @returns {ASTNode} The expression statement node.
87
+ */
37
88
  expressionStatement() {
38
89
  return {
39
90
  type: ASTType.ExpressionStatement,
40
91
  expression: this.filterChain(),
41
92
  };
42
- },
93
+ }
43
94
 
95
+ /**
96
+ * Parses a filter chain.
97
+ * @returns {ASTNode} The filter chain node.
98
+ */
44
99
  filterChain() {
45
100
  let left = this.expression();
46
101
  while (this.expect("|")) {
47
102
  left = this.filter(left);
48
103
  }
49
104
  return left;
50
- },
105
+ }
51
106
 
107
+ /**
108
+ * Parses an expression.
109
+ * @returns {ASTNode} The expression node.
110
+ */
52
111
  expression() {
53
112
  return this.assignment();
54
- },
113
+ }
55
114
 
115
+ /**
116
+ * Parses an assignment expression.
117
+ * @returns {ASTNode} The assignment expression node.
118
+ */
56
119
  assignment() {
57
120
  let result = this.ternary();
58
121
  if (this.expect("=")) {
@@ -68,8 +131,12 @@ AST.prototype = {
68
131
  };
69
132
  }
70
133
  return result;
71
- },
134
+ }
72
135
 
136
+ /**
137
+ * Parses a ternary expression.
138
+ * @returns {ASTNode} The ternary expression node.
139
+ */
73
140
  ternary() {
74
141
  const test = this.logicalOR();
75
142
  let alternate;
@@ -87,8 +154,12 @@ AST.prototype = {
87
154
  }
88
155
  }
89
156
  return test;
90
- },
157
+ }
91
158
 
159
+ /**
160
+ * Parses a logical OR expression.
161
+ * @returns {ASTNode} The logical OR expression node.
162
+ */
92
163
  logicalOR() {
93
164
  let left = this.logicalAND();
94
165
  while (this.expect("||")) {
@@ -100,8 +171,12 @@ AST.prototype = {
100
171
  };
101
172
  }
102
173
  return left;
103
- },
174
+ }
104
175
 
176
+ /**
177
+ * Parses a logical AND expression.
178
+ * @returns {ASTNode} The logical AND expression node.
179
+ */
105
180
  logicalAND() {
106
181
  let left = this.equality();
107
182
  while (this.expect("&&")) {
@@ -113,77 +188,101 @@ AST.prototype = {
113
188
  };
114
189
  }
115
190
  return left;
116
- },
191
+ }
117
192
 
193
+ /**
194
+ * Parses an equality expression.
195
+ * @returns {ASTNode} The equality expression node.
196
+ */
118
197
  equality() {
119
198
  let left = this.relational();
120
199
  let token;
121
200
  while ((token = this.expect("==", "!=", "===", "!=="))) {
122
201
  left = {
123
202
  type: ASTType.BinaryExpression,
124
- operator: token.text,
203
+ operator: /** @type {import("./lexer").Token} */ (token).text,
125
204
  left,
126
205
  right: this.relational(),
127
206
  };
128
207
  }
129
208
  return left;
130
- },
209
+ }
131
210
 
211
+ /**
212
+ * Parses a relational expression.
213
+ * @returns {ASTNode} The relational expression node.
214
+ */
132
215
  relational() {
133
216
  let left = this.additive();
134
217
  let token;
135
218
  while ((token = this.expect("<", ">", "<=", ">="))) {
136
219
  left = {
137
220
  type: ASTType.BinaryExpression,
138
- operator: token.text,
221
+ operator: /** @type {import("./lexer").Token} */ (token).text,
139
222
  left,
140
223
  right: this.additive(),
141
224
  };
142
225
  }
143
226
  return left;
144
- },
227
+ }
145
228
 
229
+ /**
230
+ * Parses an additive expression.
231
+ * @returns {ASTNode} The additive expression node.
232
+ */
146
233
  additive() {
147
234
  let left = this.multiplicative();
148
235
  let token;
149
236
  while ((token = this.expect("+", "-"))) {
150
237
  left = {
151
238
  type: ASTType.BinaryExpression,
152
- operator: token.text,
239
+ operator: /** @type {import("./lexer").Token} */ (token).text,
153
240
  left,
154
241
  right: this.multiplicative(),
155
242
  };
156
243
  }
157
244
  return left;
158
- },
245
+ }
159
246
 
247
+ /**
248
+ * Parses a multiplicative expression.
249
+ * @returns {ASTNode} The multiplicative expression node.
250
+ */
160
251
  multiplicative() {
161
252
  let left = this.unary();
162
253
  let token;
163
254
  while ((token = this.expect("*", "/", "%"))) {
164
255
  left = {
165
256
  type: ASTType.BinaryExpression,
166
- operator: token.text,
257
+ operator: /** @type {import("./lexer").Token} */ (token).text,
167
258
  left,
168
259
  right: this.unary(),
169
260
  };
170
261
  }
171
262
  return left;
172
- },
263
+ }
173
264
 
265
+ /**
266
+ * Parses a unary expression.
267
+ * @returns {ASTNode} The unary expression node.
268
+ */
174
269
  unary() {
175
270
  let token;
176
271
  if ((token = this.expect("+", "-", "!"))) {
177
272
  return {
178
273
  type: ASTType.UnaryExpression,
179
- operator: token.text,
274
+ operator: /** @type {import("./lexer").Token} */ (token).text,
180
275
  prefix: true,
181
276
  argument: this.unary(),
182
277
  };
183
278
  }
184
279
  return this.primary();
185
- },
280
+ }
186
281
 
282
+ /**
283
+ * Parses a primary expression.
284
+ * @returns {ASTNode} The primary expression node.
285
+ */
187
286
  primary() {
188
287
  let primary;
189
288
  if (this.expect("(")) {
@@ -196,38 +295,43 @@ AST.prototype = {
196
295
  } else if (
197
296
  Object.prototype.hasOwnProperty.call(
198
297
  this.selfReferential,
199
- this.peek().text,
298
+ /** @type {import("./lexer").Token} */ (this.peek()).text,
200
299
  )
201
300
  ) {
202
301
  primary = structuredClone(this.selfReferential[this.consume().text]);
203
302
  } else if (
204
303
  Object.prototype.hasOwnProperty.call(
205
304
  this.options.literals,
206
- this.peek().text,
305
+ /** @type {import("./lexer").Token} */ (this.peek()).text,
207
306
  )
208
307
  ) {
209
308
  primary = {
210
309
  type: ASTType.Literal,
211
310
  value: this.options.literals[this.consume().text],
212
311
  };
213
- } else if (this.peek().identifier) {
312
+ } else if (
313
+ /** @type {import("./lexer").Token} */ (this.peek()).identifier
314
+ ) {
214
315
  primary = this.identifier();
215
- } else if (this.peek().constant) {
316
+ } else if (/** @type {import("./lexer").Token} */ (this.peek()).constant) {
216
317
  primary = this.constant();
217
318
  } else {
218
- this.throwError("not a primary expression", this.peek());
319
+ this.throwError(
320
+ "not a primary expression",
321
+ /** @type {import("./lexer").Token} */ (this.peek()),
322
+ );
219
323
  }
220
324
 
221
325
  let next;
222
326
  while ((next = this.expect("(", "[", "."))) {
223
- if (next.text === "(") {
327
+ if (/** @type {import("./lexer").Token} */ (next).text === "(") {
224
328
  primary = {
225
329
  type: ASTType.CallExpression,
226
330
  callee: primary,
227
331
  arguments: this.parseArguments(),
228
332
  };
229
333
  this.consume(")");
230
- } else if (next.text === "[") {
334
+ } else if (/** @type {import("./lexer").Token} */ (next).text === "[") {
231
335
  primary = {
232
336
  type: ASTType.MemberExpression,
233
337
  object: primary,
@@ -235,7 +339,7 @@ AST.prototype = {
235
339
  computed: true,
236
340
  };
237
341
  this.consume("]");
238
- } else if (next.text === ".") {
342
+ } else if (/** @type {import("./lexer").Token} */ (next).text === ".") {
239
343
  primary = {
240
344
  type: ASTType.MemberExpression,
241
345
  object: primary,
@@ -247,9 +351,15 @@ AST.prototype = {
247
351
  }
248
352
  }
249
353
  return primary;
250
- },
354
+ }
251
355
 
356
+ /**
357
+ * Parses a filter.
358
+ * @param {ASTNode} baseExpression - The base expression to apply the filter to.
359
+ * @returns {ASTNode} The filter node.
360
+ */
252
361
  filter(baseExpression) {
362
+ /** @type {ASTNode[]} */
253
363
  const args = [baseExpression];
254
364
  const result = {
255
365
  type: ASTType.CallExpression,
@@ -263,9 +373,14 @@ AST.prototype = {
263
373
  }
264
374
 
265
375
  return result;
266
- },
376
+ }
267
377
 
378
+ /**
379
+ * Parses function arguments.
380
+ * @returns {ASTNode[]} The arguments array.
381
+ */
268
382
  parseArguments() {
383
+ /** @type {ASTNode[]} */
269
384
  const args = [];
270
385
  if (this.peekToken().text !== ")") {
271
386
  do {
@@ -273,22 +388,35 @@ AST.prototype = {
273
388
  } while (this.expect(","));
274
389
  }
275
390
  return args;
276
- },
391
+ }
277
392
 
393
+ /**
394
+ * Parses an identifier.
395
+ * @returns {ASTNode} The identifier node.
396
+ */
278
397
  identifier() {
279
398
  const token = this.consume();
280
399
  if (!token.identifier) {
281
400
  this.throwError("is not a valid identifier", token);
282
401
  }
283
402
  return { type: ASTType.Identifier, name: token.text };
284
- },
403
+ }
285
404
 
405
+ /**
406
+ * Parses a constant.
407
+ * @returns {ASTNode} The constant node.
408
+ */
286
409
  constant() {
287
410
  // TODO check that it is a constant
288
411
  return { type: ASTType.Literal, value: this.consume().value };
289
- },
412
+ }
290
413
 
414
+ /**
415
+ * Parses an array declaration.
416
+ * @returns {ASTNode} The array declaration node.
417
+ */
291
418
  arrayDeclaration() {
419
+ /** @type {ASTNode[]} */
292
420
  const elements = [];
293
421
  if (this.peekToken().text !== "]") {
294
422
  do {
@@ -302,10 +430,16 @@ AST.prototype = {
302
430
  this.consume("]");
303
431
 
304
432
  return { type: ASTType.ArrayExpression, elements };
305
- },
433
+ }
306
434
 
435
+ /**
436
+ * Parses an object.
437
+ * @returns {ASTNode} The object node.
438
+ */
307
439
  object() {
440
+ /** @type {ASTNode[]} */
308
441
  const properties = [];
442
+ /** @type {ASTNode} */
309
443
  let property;
310
444
  if (this.peekToken().text !== "}") {
311
445
  do {
@@ -314,12 +448,14 @@ AST.prototype = {
314
448
  break;
315
449
  }
316
450
  property = { type: ASTType.Property, kind: "init" };
317
- if (this.peek().constant) {
451
+ if (/** @type {import("./lexer").Token} */ (this.peek()).constant) {
318
452
  property.key = this.constant();
319
453
  property.computed = false;
320
454
  this.consume(":");
321
455
  property.value = this.expression();
322
- } else if (this.peek().identifier) {
456
+ } else if (
457
+ /** @type {import("./lexer").Token} */ (this.peek()).identifier
458
+ ) {
323
459
  property.key = this.identifier();
324
460
  property.computed = false;
325
461
  if (this.peek(":")) {
@@ -336,7 +472,10 @@ AST.prototype = {
336
472
  this.consume(":");
337
473
  property.value = this.expression();
338
474
  } else {
339
- this.throwError("invalid key", this.peek());
475
+ this.throwError(
476
+ "invalid key",
477
+ /** @type {import("./lexer").Token} */ (this.peek()),
478
+ );
340
479
  }
341
480
  properties.push(property);
342
481
  } while (this.expect(","));
@@ -344,8 +483,13 @@ AST.prototype = {
344
483
  this.consume("}");
345
484
 
346
485
  return { type: ASTType.ObjectExpression, properties };
347
- },
486
+ }
348
487
 
488
+ /**
489
+ * Throws a syntax error.
490
+ * @param {string} msg - The error message.
491
+ * @param {import("./lexer").Token} [token] - The token that caused the error.
492
+ */
349
493
  throwError(msg, token) {
350
494
  throw $parseMinErr(
351
495
  "syntax",
@@ -356,8 +500,13 @@ AST.prototype = {
356
500
  this.text,
357
501
  this.text.substring(token.index),
358
502
  );
359
- },
503
+ }
360
504
 
505
+ /**
506
+ * Consumes a token if it matches the expected type.
507
+ * @param {string} [e1] - The expected token type.
508
+ * @returns {import("./lexer").Token} The consumed token.
509
+ */
361
510
  consume(e1) {
362
511
  if (this.tokens.length === 0) {
363
512
  throw $parseMinErr(
@@ -369,11 +518,19 @@ AST.prototype = {
369
518
 
370
519
  const token = this.expect(e1);
371
520
  if (!token) {
372
- this.throwError(`is unexpected, expecting [${e1}]`, this.peek());
521
+ this.throwError(
522
+ `is unexpected, expecting [${e1}]`,
523
+ /** @type {import("./lexer").Token} */ (this.peek()),
524
+ );
525
+ } else {
526
+ return /** @type {import("./lexer").Token} */ (token);
373
527
  }
374
- return token;
375
- },
528
+ }
376
529
 
530
+ /**
531
+ * Returns the next token without consuming it.
532
+ * @returns {import("./lexer").Token} The next token.
533
+ */
377
534
  peekToken() {
378
535
  if (this.tokens.length === 0) {
379
536
  throw $parseMinErr(
@@ -383,40 +540,48 @@ AST.prototype = {
383
540
  );
384
541
  }
385
542
  return this.tokens[0];
386
- },
387
-
388
- peek(e1, e2, e3, e4) {
389
- return this.peekAhead(0, e1, e2, e3, e4);
390
- },
391
-
392
- peekAhead(i, e1, e2, e3, e4) {
543
+ }
544
+
545
+ /**
546
+ * Checks if the next token matches any of the expected types.
547
+ * @param {...string} [expected] - The expected token types.
548
+ * @returns {import('./lexer').Token|boolean} The next token if it matches, otherwise false.
549
+ */
550
+ peek(...expected) {
551
+ return this.peekAhead(0, ...expected);
552
+ }
553
+
554
+ /**
555
+ * Checks if the token at the specified index matches any of the expected types.
556
+ * @param {number} i - The index to check.
557
+ * @param {...string} [expected] - The expected token types.
558
+ * @returns {import("./lexer").Token|boolean} The token at the specified index if it matches, otherwise false.
559
+ */
560
+ peekAhead(i, ...expected) {
393
561
  if (this.tokens.length > i) {
394
562
  const token = this.tokens[i];
395
563
  const t = token.text;
396
564
  if (
397
- t === e1 ||
398
- t === e2 ||
399
- t === e3 ||
400
- t === e4 ||
401
- (!e1 && !e2 && !e3 && !e4)
565
+ expected.includes(t) ||
566
+ (!expected[0] && !expected[1] && !expected[2] && !expected[3])
402
567
  ) {
403
568
  return token;
404
569
  }
405
570
  }
406
571
  return false;
407
- },
408
-
409
- expect(e1, e2, e3, e4) {
410
- const token = this.peek(e1, e2, e3, e4);
572
+ }
573
+
574
+ /**
575
+ * Consumes the next token if it matches any of the expected types.
576
+ * @param {...string} [expected] - The expected token types.
577
+ * @returns {import("./lexer").Token|boolean} The consumed token if it matches, otherwise false.
578
+ */
579
+ expect(...expected) {
580
+ const token = this.peek(...expected);
411
581
  if (token) {
412
582
  this.tokens.shift();
413
583
  return token;
414
584
  }
415
585
  return false;
416
- },
417
-
418
- selfReferential: {
419
- this: { type: ASTType.ThisExpression },
420
- $locals: { type: ASTType.LocalsExpression },
421
- },
422
- };
586
+ }
587
+ }