@litert/typeguard 1.0.1 → 1.3.0-dev.0

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 (76) hide show
  1. package/CHANGES.md +16 -0
  2. package/lib/BuiltInTypeCompiler.d.ts +4 -4
  3. package/lib/BuiltInTypeCompiler.d.ts.map +1 -1
  4. package/lib/BuiltInTypeCompiler.js +167 -166
  5. package/lib/BuiltInTypeCompiler.js.map +1 -1
  6. package/lib/BuiltInTypes.d.ts +1 -1
  7. package/lib/BuiltInTypes.js +37 -36
  8. package/lib/BuiltInTypes.js.map +1 -1
  9. package/lib/Common.d.ts +27 -10
  10. package/lib/Common.d.ts.map +1 -1
  11. package/lib/Common.js +1 -1
  12. package/lib/Compiler.d.ts +2 -2
  13. package/lib/Compiler.d.ts.map +1 -1
  14. package/lib/Compiler.js +159 -102
  15. package/lib/Compiler.js.map +1 -1
  16. package/lib/Context.d.ts +6 -5
  17. package/lib/Context.d.ts.map +1 -1
  18. package/lib/Context.js +11 -7
  19. package/lib/Context.js.map +1 -1
  20. package/lib/FilterCompiler.d.ts +5 -5
  21. package/lib/FilterCompiler.d.ts.map +1 -1
  22. package/lib/FilterCompiler.js +25 -24
  23. package/lib/FilterCompiler.js.map +1 -1
  24. package/lib/InlineCompiler.d.ts +12 -5
  25. package/lib/InlineCompiler.d.ts.map +1 -1
  26. package/lib/InlineCompiler.js +18 -6
  27. package/lib/InlineCompiler.js.map +1 -1
  28. package/lib/Internal.d.ts +6 -4
  29. package/lib/Internal.d.ts.map +1 -1
  30. package/lib/Internal.js +12 -10
  31. package/lib/Internal.js.map +1 -1
  32. package/lib/Modifiers.d.ts +1 -1
  33. package/lib/Modifiers.js +2 -1
  34. package/lib/Modifiers.js.map +1 -1
  35. package/lib/index.d.ts +5 -5
  36. package/lib/index.js +19 -7
  37. package/lib/index.js.map +1 -1
  38. package/lib/langs/JavaScript.d.ts +2 -2
  39. package/lib/langs/JavaScript.d.ts.map +1 -1
  40. package/lib/langs/JavaScript.js +55 -29
  41. package/lib/langs/JavaScript.js.map +1 -1
  42. package/package.json +17 -13
  43. package/src/examples/quick-start.ts +172 -0
  44. package/src/lib/BuiltInTypeCompiler.ts +171 -171
  45. package/src/lib/BuiltInTypes.ts +36 -36
  46. package/src/lib/Common.ts +49 -10
  47. package/src/lib/Compiler.ts +354 -247
  48. package/src/lib/Context.ts +11 -13
  49. package/src/lib/FilterCompiler.ts +84 -84
  50. package/src/lib/InlineCompiler.ts +41 -15
  51. package/src/lib/Internal.ts +17 -13
  52. package/src/lib/Modifiers.ts +2 -2
  53. package/src/lib/index.ts +5 -5
  54. package/src/lib/langs/JavaScript.ts +84 -31
  55. package/src/test/00-all.ts +10 -10
  56. package/src/test/01-elemental-types.ts +1111 -1110
  57. package/src/test/02-array-and-list.ts +75 -75
  58. package/src/test/03-tuple.ts +87 -87
  59. package/src/test/04-from-string.ts +849 -848
  60. package/src/test/05-string-asserts.ts +422 -422
  61. package/src/test/06-structure.ts +107 -107
  62. package/src/test/07-modifiers.ts +151 -42
  63. package/src/test/08-map-and-dict.ts +46 -46
  64. package/src/test/09-exceptions.ts +30 -9
  65. package/src/test/abstracts.ts +83 -45
  66. package/dist/typeguard.amd.d.ts +0 -588
  67. package/dist/typeguard.amd.d.ts.map +0 -1
  68. package/dist/typeguard.amd.js +0 -2069
  69. package/dist/typeguard.amd.js.map +0 -1
  70. package/dist/typeguard.system.d.ts +0 -588
  71. package/dist/typeguard.system.d.ts.map +0 -1
  72. package/dist/typeguard.system.js +0 -2185
  73. package/dist/typeguard.system.js.map +0 -1
  74. package/src/samples/quick-start.ts +0 -52
  75. package/tsconfig-amd.json +0 -72
  76. package/tsconfig-systemjs.json +0 -72
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2019 Angus.Fenying <fenying@litert.org>
2
+ * Copyright 2022 Angus Fenying <fenying@litert.org>
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -14,23 +14,22 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import * as I from "./Internal";
18
- import * as C from "./Common";
19
- import * as M from "./Modifiers";
20
- import { Context } from "./Context";
21
- import * as B from "./BuiltInTypes";
22
- import { BuiltInTypeCompiler } from "./BuiltInTypeCompiler";
23
- import { FilterCompiler } from "./FilterCompiler";
17
+ import * as I from './Internal';
18
+ import * as C from './Common';
19
+ import * as M from './Modifiers';
20
+ import { Context } from './Context';
21
+ import * as B from './BuiltInTypes';
22
+ import { BuiltInTypeCompiler } from './BuiltInTypeCompiler';
23
+ import { FilterCompiler } from './FilterCompiler';
24
24
 
25
- class Compiler
26
- implements C.ICompiler {
25
+ class Compiler implements C.ICompiler {
27
26
 
28
27
  private _defTypes: Record<string, C.ICompileResult>;
29
28
 
30
29
  public constructor(
31
- private _lang: C.ILanguageBuilder,
32
- private _builtInTypes: I.IBuiltInTypeCompiler,
33
- private _filters: I.IFilterCompiler
30
+ private readonly _lang: C.ILanguageBuilder,
31
+ private readonly _builtInTypes: I.IBuiltInTypeCompiler,
32
+ private readonly _filters: I.IFilterCompiler
34
33
  ) {
35
34
  this._defTypes = {};
36
35
  }
@@ -45,13 +44,44 @@ implements C.ICompiler {
45
44
  return null;
46
45
  }
47
46
 
47
+ private _addTrace(ctx: I.IContext): string {
48
+
49
+ if (!ctx.vTraceName) {
50
+
51
+ return this._lang.literalFalse;
52
+ }
53
+
54
+ return this._lang.addTrace(
55
+ ctx.vTraceName,
56
+ ctx.vTracePrefix,
57
+ ctx.tracePath
58
+ );
59
+ }
60
+
61
+ private _addTraceOr(ctx: I.IContext, expr: string, subPath: string = ''): string {
62
+
63
+ if (!ctx.vTraceName) {
64
+
65
+ return expr;
66
+ }
67
+
68
+ return this._lang.orAddTrace(
69
+ expr,
70
+ ctx.vTraceName,
71
+ ctx.vTracePrefix,
72
+ `${ctx.tracePath}${subPath}`
73
+ );
74
+ }
75
+
48
76
  public compile(options: C.ICompileOptions): C.ICompileResult {
49
77
 
50
78
  const referredTypes: Record<string, true> = {};
51
79
 
52
80
  const ctx: I.IContext = new Context(
53
- this._lang.varName("entry"),
54
- this._lang.varName("types"),
81
+ this._lang.varName('entry'),
82
+ options.traceErrors ? this._lang.varName('failedAsserts') : '',
83
+ options.traceErrors ? this._lang.varName('tracePrefix') : '',
84
+ this._lang.varName('types'),
55
85
  referredTypes
56
86
  );
57
87
 
@@ -68,15 +98,31 @@ implements C.ICompiler {
68
98
 
69
99
  const ret: C.ICompileResult = {
70
100
 
71
- source: "",
101
+ source: '',
72
102
  arguments: [{
73
- "name": ctx.vName,
74
- "type": "unknown"
103
+ 'name': ctx.vName,
104
+ 'type': 'unknown',
105
+ 'initial': ''
75
106
  }],
76
107
  typeSlotName: ctx.typeSlotName,
77
108
  referredTypes: []
78
109
  };
79
110
 
111
+ if (ctx.vTraceName) {
112
+
113
+ ret.arguments.push({
114
+ 'name': ctx.vTraceName,
115
+ 'type': 'string[]',
116
+ 'initial': '[]'
117
+ });
118
+
119
+ ret.arguments.push({
120
+ 'name': ctx.vTracePrefix,
121
+ 'type': 'string',
122
+ 'initial': this._lang.literal('data')
123
+ });
124
+ }
125
+
80
126
  ret.source = this._compile(
81
127
  ctx,
82
128
  options.rule
@@ -91,82 +137,85 @@ implements C.ICompiler {
91
137
  private _compile(ctx: I.IContext, rules: any): string {
92
138
 
93
139
  switch (typeof rules) {
94
- case "string":
140
+ case 'string':
95
141
 
96
- return this._compileStringRule(ctx, rules);
142
+ return this._compileStringRule(ctx, rules);
97
143
 
98
- case "boolean":
144
+ case 'boolean':
99
145
 
100
- if (ctx.flags[I.EFlags.FROM_STRING]) {
101
-
102
- return this._lang.or([
103
- this._lang.eq(
104
- ctx.vName,
105
- this._lang.literal(rules)
106
- ),
107
- this._lang.eq(
108
- this._lang.str2Bool(ctx.vName),
109
- this._lang.literal(rules)
110
- )
111
- ]);
112
- }
113
-
114
- return this._lang.eq(
115
- ctx.vName,
116
- this._lang.literal(rules)
117
- );
118
-
119
- case "number":
120
-
121
- if (ctx.flags[I.EFlags.FROM_STRING]) {
122
-
123
- return this._lang.or([
124
- this._lang.eq(
125
- ctx.vName,
126
- this._lang.literal(rules)
127
- ),
128
- this._lang.eq(
129
- this._lang.str2Float(ctx.vName),
130
- this._lang.literal(rules)
131
- )
132
- ]);
133
- }
134
-
135
- return this._lang.eq(
136
- ctx.vName,
137
- this._lang.literal(rules)
138
- );
146
+ if (ctx.flags[I.EFlags.FROM_STRING]) {
139
147
 
140
- case "object":
148
+ return this._lang.or([
149
+ this._lang.eq(
150
+ ctx.vName,
151
+ this._lang.literal(rules)
152
+ ),
153
+ this._lang.eq(
154
+ this._lang.str2Bool(ctx.vName),
155
+ this._lang.literal(rules)
156
+ )
157
+ ]);
158
+ }
141
159
 
142
- if (Array.isArray(rules)) {
160
+ return this._lang.eq(
161
+ ctx.vName,
162
+ this._lang.literal(rules)
163
+ );
143
164
 
144
- return this._compileModifiedRule(ctx, rules);
145
- }
146
- else if (rules === null) {
165
+ case 'number':
147
166
 
148
167
  if (ctx.flags[I.EFlags.FROM_STRING]) {
149
168
 
150
169
  return this._lang.or([
151
- this._lang.isNull(ctx.vName, true),
152
170
  this._lang.eq(
153
171
  ctx.vName,
154
- this._lang.literal("null")
172
+ this._lang.literal(rules)
173
+ ),
174
+ this._lang.eq(
175
+ this._lang.str2Float(ctx.vName),
176
+ this._lang.literal(rules)
155
177
  )
156
178
  ]);
157
179
  }
158
180
 
159
- return this._lang.isNull(ctx.vName, true);
160
- }
181
+ return this._lang.eq(
182
+ ctx.vName,
183
+ this._lang.literal(rules)
184
+ );
185
+
186
+ case 'object':
161
187
 
162
- return this._compileStructuredRule(ctx, rules);
188
+ if (Array.isArray(rules)) {
163
189
 
164
- case "undefined":
190
+ return this._compileModifiedRule(ctx, rules);
191
+ }
192
+ else if (rules === null) {
193
+
194
+ if (ctx.flags[I.EFlags.FROM_STRING]) {
195
+
196
+ return this._lang.or([
197
+ this._lang.isNull(ctx.vName, true),
198
+ this._lang.eq(
199
+ ctx.vName,
200
+ this._lang.literal('null')
201
+ )
202
+ ]);
203
+ }
165
204
 
166
- return this._lang.isUndefined(ctx.vName, true);
205
+ return this._lang.isNull(ctx.vName, true);
206
+ }
207
+
208
+ return this._compileStructuredRule(ctx, rules);
209
+
210
+ case 'undefined':
211
+
212
+ return this._lang.isUndefined(ctx.vName, true);
213
+
214
+ default:
215
+ break;
167
216
  }
168
217
 
169
- throw new TypeError("Unknwn rules.");
218
+ throw new TypeError('Unknwn rules.');
170
219
  }
171
220
 
172
221
  private _compileStringRule(ctx: I.IContext, rule: string): string {
@@ -181,7 +230,7 @@ implements C.ICompiler {
181
230
  ]);
182
231
  }
183
232
 
184
- if (rule[0] === I.IMPLICIT_SYMBOL) {
233
+ if (rule.startsWith(I.IMPLICIT_SYMBOL)) {
185
234
 
186
235
  return this._lang.or([
187
236
  this._builtInTypes.compile(B.VOID, ctx, []),
@@ -189,61 +238,73 @@ implements C.ICompiler {
189
238
  ]);
190
239
  }
191
240
 
192
- if (rule[0] === I.NEGATIVE_SYMBOL) {
241
+ if (rule.startsWith(I.NEGATIVE_SYMBOL)) {
193
242
 
194
243
  return this._lang.not(this._compileStringRule(ctx, rule.slice(1)));
195
244
  }
196
245
 
197
- let regResult: RegExpMatchArray | null;
246
+ let regResult: RegExpMatchArray | null = /\[\s*(\d*|\d+\s*,\s*\d*)\s*\]$/.exec(rule);
198
247
 
199
248
  /**
200
- * For rules like `xxx[123]` or `xxx[1,5]`.
249
+ * For rules like `xxx[123]` or `xxx[1,5]` or `xxx[1,]`.
201
250
  */
202
- if (regResult = rule.match(/\[\s*(\d*|\d+\s*,\s*\d*)\s*\]$/)) {
251
+ if (regResult) {
203
252
 
204
253
  if (regResult[1]) {
205
254
 
206
- let range = regResult[1].split(",").map((x) => parseInt(x.trim()));
255
+ const range = regResult[1].split(',').map((x) => parseInt(x.trim()));
207
256
 
208
257
  if (range.length === 1) {
209
258
 
259
+ /**
260
+ * For rules like `xxx[123]`.
261
+ */
210
262
  return this._compileModifiedRule(ctx, [
211
263
  M.ARRAY,
212
264
  range[0],
213
- rule.substr(0, regResult.index)
265
+ rule.slice(0, regResult.index)
214
266
  ]);
215
267
  }
216
268
  else if (Number.isNaN(range[1])) {
217
269
 
270
+ /**
271
+ * For rules like `xxx[1,]`.
272
+ */
218
273
  return this._compileModifiedRule(ctx, [
219
274
  M.ARRAY,
220
275
  [range[0]],
221
- rule.substr(0, regResult.index)
276
+ rule.slice(0, regResult.index)
222
277
  ]);
223
278
  }
224
279
  else {
225
280
 
281
+ /**
282
+ * For rules like `xxx[1,5]`.
283
+ */
226
284
  return this._compileModifiedRule(ctx, [
227
285
  M.ARRAY,
228
286
  range,
229
- rule.substr(0, regResult.index)
287
+ rule.slice(0, regResult.index)
230
288
  ]);
231
289
  }
232
290
  }
233
291
  else {
234
292
 
293
+ /**
294
+ * For rules like `xxx[]`.
295
+ */
235
296
  return this._compileModifiedRule(ctx, [
236
297
  M.LIST,
237
- rule.substr(0, regResult.index)
298
+ rule.slice(0, regResult.index)
238
299
  ]);
239
300
  }
240
301
  }
241
302
 
242
- /**
243
- * For rules like `xxx{}`.
244
- */
245
303
  if (rule.endsWith(I.MAP_SUFFIX)) {
246
304
 
305
+ /**
306
+ * For rules like `xxx{}`.
307
+ */
247
308
  return this._lang.and([
248
309
  this._compileModifiedRule(ctx, [
249
310
  M.MAP,
@@ -252,11 +313,13 @@ implements C.ICompiler {
252
313
  ]);
253
314
  }
254
315
 
255
- if (rule[0] === I.PREDEF_TYPE_SYMBOL) {
316
+ if (rule.startsWith(I.PREDEF_TYPE_SYMBOL)) {
256
317
 
257
318
  return this._usePredefinedType(ctx, rule.slice(1));
258
319
  }
259
320
 
321
+ regResult = /^(\w+)(\(\s*(-?\d+(\.\d+)?)?\s*,?\s*(-?\d+(\.\d+)?)?\s*\))?$/.exec(rule);
322
+
260
323
  /**
261
324
  * For built-in-type rules like:
262
325
  *
@@ -265,11 +328,11 @@ implements C.ICompiler {
265
328
  * - `string`
266
329
  * - `int(12, 34)`
267
330
  */
268
- if (regResult = rule.match(/^(\w+)(\(\s*(-?\d+(\.\d+)?)?\s*,?\s*(-?\d+(\.\d+)?)?\s*\))?$/)) {
331
+ if (regResult) {
269
332
 
270
333
  if (regResult[2]) {
271
334
 
272
- const args = regResult[2].slice(1, -1).trim().split(",").map(
335
+ const args = regResult[2].slice(1, -1).trim().split(',').map(
273
336
  (x) => parseFloat(x.trim())
274
337
  );
275
338
 
@@ -287,7 +350,7 @@ implements C.ICompiler {
287
350
  );
288
351
  }
289
352
 
290
- if (rule[0] === I.FILTER_PREFIX) {
353
+ if (rule.startsWith(I.FILTER_PREFIX)) {
291
354
 
292
355
  return this._filters.compile(rule, ctx);
293
356
  }
@@ -318,162 +381,162 @@ implements C.ICompiler {
318
381
  rule: string
319
382
  ): string | false {
320
383
 
321
- const assertRule = rule.match(/^:([-\w]+):/);
384
+ const assertRule = /^:([-\w]+):/.exec(rule);
322
385
 
323
386
  const offset = assertRule ? assertRule[1].length + 2 : 2;
324
387
 
325
- switch ((assertRule && assertRule[1]) || rule.substr(0, 2)) {
326
- case "==":
327
- case "equal":
388
+ switch ((assertRule?.[1]) ?? rule.slice(0, 2)) {
389
+ case '==':
390
+ case 'equal':
328
391
 
329
- return this._lang.eq(
330
- ctx.vName,
331
- this._lang.literal(rule.slice(offset))
332
- );
392
+ return this._lang.eq(
393
+ ctx.vName,
394
+ this._lang.literal(rule.slice(offset))
395
+ );
333
396
 
334
- case "%=":
335
- case "equal-i":
397
+ case '%=':
398
+ case 'equal-i':
336
399
 
337
- return this._lang.eq(
338
- this._lang.lowerCase(ctx.vName),
339
- this._lang.literal(rule.slice(offset).toLowerCase())
340
- );
400
+ return this._lang.eq(
401
+ this._lang.lowerCase(ctx.vName),
402
+ this._lang.literal(rule.slice(offset).toLowerCase())
403
+ );
341
404
 
342
- case "!=":
343
- case "not-equal":
405
+ case '!=':
406
+ case 'not-equal':
344
407
 
345
- return this._lang.ne(
346
- ctx.vName,
347
- this._lang.literal(rule.slice(offset))
348
- );
408
+ return this._lang.ne(
409
+ ctx.vName,
410
+ this._lang.literal(rule.slice(offset))
411
+ );
349
412
 
350
- case "%!":
351
- case "not-equal-i":
413
+ case '%!':
414
+ case 'not-equal-i':
352
415
 
353
- return this._lang.ne(
354
- this._lang.lowerCase(ctx.vName),
355
- this._lang.literal(rule.slice(offset).toLowerCase())
356
- );
416
+ return this._lang.ne(
417
+ this._lang.lowerCase(ctx.vName),
418
+ this._lang.literal(rule.slice(offset).toLowerCase())
419
+ );
357
420
 
358
- case "~=":
359
- case "match":
421
+ case '~=':
422
+ case 'match':
360
423
 
361
- return this._lang.matchRegExp(
362
- ctx.vName,
363
- rule.slice(offset)
364
- );
424
+ return this._lang.matchRegExp(
425
+ ctx.vName,
426
+ rule.slice(offset)
427
+ );
365
428
 
366
- case "~!":
367
- case "not-match":
429
+ case '~!':
430
+ case 'not-match':
368
431
 
369
- return this._lang.not(this._lang.matchRegExp(
370
- ctx.vName,
371
- rule.slice(offset)
372
- ));
432
+ return this._lang.not(this._lang.matchRegExp(
433
+ ctx.vName,
434
+ rule.slice(offset)
435
+ ));
373
436
 
374
- case "?=":
375
- case "include":
437
+ case '?=':
438
+ case 'include':
376
439
 
377
- return this._lang.instr(
378
- ctx.vName,
379
- this._lang.literal(rule.slice(offset))
380
- );
440
+ return this._lang.instr(
441
+ ctx.vName,
442
+ this._lang.literal(rule.slice(offset))
443
+ );
381
444
 
382
- case "?!":
383
- case "not-include":
445
+ case '?!':
446
+ case 'not-include':
384
447
 
385
- return this._lang.not(this._lang.instr(
386
- ctx.vName,
387
- this._lang.literal(rule.slice(offset))
388
- ));
448
+ return this._lang.not(this._lang.instr(
449
+ ctx.vName,
450
+ this._lang.literal(rule.slice(offset))
451
+ ));
389
452
 
390
- case "*=":
391
- case "include-i":
453
+ case '*=':
454
+ case 'include-i':
392
455
 
393
- return this._lang.instr(
394
- this._lang.lowerCase(ctx.vName),
395
- this._lang.literal(rule.slice(offset).toLowerCase())
396
- );
456
+ return this._lang.instr(
457
+ this._lang.lowerCase(ctx.vName),
458
+ this._lang.literal(rule.slice(offset).toLowerCase())
459
+ );
397
460
 
398
- case "*!":
399
- case "not-include-i":
461
+ case '*!':
462
+ case 'not-include-i':
400
463
 
401
- return this._lang.not(this._lang.instr(
402
- this._lang.lowerCase(ctx.vName),
403
- this._lang.literal(rule.slice(offset).toLowerCase())
404
- ));
464
+ return this._lang.not(this._lang.instr(
465
+ this._lang.lowerCase(ctx.vName),
466
+ this._lang.literal(rule.slice(offset).toLowerCase())
467
+ ));
405
468
 
406
- case "^=":
407
- case "start-with":
469
+ case '^=':
470
+ case 'start-with':
408
471
 
409
- return this._lang.startsWith(
410
- ctx.vName,
411
- this._lang.literal(rule.slice(offset))
412
- );
472
+ return this._lang.startsWith(
473
+ ctx.vName,
474
+ this._lang.literal(rule.slice(offset))
475
+ );
413
476
 
414
- case "start-with-i":
477
+ case 'start-with-i':
415
478
 
416
- return this._lang.startsWith(
417
- this._lang.lowerCase(ctx.vName),
418
- this._lang.literal(rule.slice(offset).toLowerCase())
419
- );
479
+ return this._lang.startsWith(
480
+ this._lang.lowerCase(ctx.vName),
481
+ this._lang.literal(rule.slice(offset).toLowerCase())
482
+ );
420
483
 
421
- case "^!":
422
- case "not-start-with":
484
+ case '^!':
485
+ case 'not-start-with':
423
486
 
424
- return this._lang.not(this._lang.startsWith(
425
- ctx.vName,
426
- this._lang.literal(rule.slice(offset))
427
- ));
487
+ return this._lang.not(this._lang.startsWith(
488
+ ctx.vName,
489
+ this._lang.literal(rule.slice(offset))
490
+ ));
428
491
 
429
- case "not-start-with-i":
492
+ case 'not-start-with-i':
430
493
 
431
- return this._lang.not(this._lang.startsWith(
432
- this._lang.lowerCase(ctx.vName),
433
- this._lang.literal(rule.slice(offset).toLowerCase())
434
- ));
494
+ return this._lang.not(this._lang.startsWith(
495
+ this._lang.lowerCase(ctx.vName),
496
+ this._lang.literal(rule.slice(offset).toLowerCase())
497
+ ));
435
498
 
436
- case "$=":
437
- case "end-with":
499
+ case '$=':
500
+ case 'end-with':
438
501
 
439
- return this._lang.endsWith(
440
- ctx.vName,
441
- this._lang.literal(rule.slice(offset))
442
- );
502
+ return this._lang.endsWith(
503
+ ctx.vName,
504
+ this._lang.literal(rule.slice(offset))
505
+ );
443
506
 
444
- case "end-with-i":
507
+ case 'end-with-i':
445
508
 
446
- return this._lang.endsWith(
447
- this._lang.lowerCase(ctx.vName),
448
- this._lang.literal(rule.slice(offset).toLowerCase())
449
- );
509
+ return this._lang.endsWith(
510
+ this._lang.lowerCase(ctx.vName),
511
+ this._lang.literal(rule.slice(offset).toLowerCase())
512
+ );
450
513
 
451
- case "$!":
452
- case "not-end-with":
514
+ case '$!':
515
+ case 'not-end-with':
453
516
 
454
- return this._lang.not(this._lang.endsWith(
455
- ctx.vName,
456
- this._lang.literal(rule.slice(offset))
457
- ));
517
+ return this._lang.not(this._lang.endsWith(
518
+ ctx.vName,
519
+ this._lang.literal(rule.slice(offset))
520
+ ));
458
521
 
459
- case "not-end-with-i":
522
+ case 'not-end-with-i':
460
523
 
461
- return this._lang.not(this._lang.endsWith(
462
- this._lang.lowerCase(ctx.vName),
463
- this._lang.literal(rule.slice(offset).toLowerCase())
464
- ));
524
+ return this._lang.not(this._lang.endsWith(
525
+ this._lang.lowerCase(ctx.vName),
526
+ this._lang.literal(rule.slice(offset).toLowerCase())
527
+ ));
465
528
 
466
- default:
529
+ default:
467
530
 
468
- if (rule.startsWith("=")) {
531
+ if (rule.startsWith('=')) {
469
532
 
470
- return this._lang.eq(ctx.vName, this._lang.literal(rule.slice(1)));
471
- }
533
+ return this._lang.eq(ctx.vName, this._lang.literal(rule.slice(1)));
534
+ }
472
535
 
473
- if (rule.startsWith("~")) {
536
+ if (rule.startsWith('~')) {
474
537
 
475
- return this._lang.matchRegExp(ctx.vName, rule.slice(1));
476
- }
538
+ return this._lang.matchRegExp(ctx.vName, rule.slice(1));
539
+ }
477
540
  }
478
541
 
479
542
  return false;
@@ -483,14 +546,14 @@ implements C.ICompiler {
483
546
 
484
547
  if (!rules.length) {
485
548
 
486
- throw new TypeError(`Unknwon type "[]".`);
549
+ throw new TypeError('Unknwon type "[]".');
487
550
  }
488
551
 
489
552
  /**
490
553
  * By default, use OR modifier.
491
554
  */
492
555
  if (
493
- typeof rules[0] !== "string" ||
556
+ typeof rules[0] !== 'string' ||
494
557
  !rules[0].startsWith(I.MODIFIER_PREFIX)
495
558
  ) {
496
559
 
@@ -574,7 +637,7 @@ implements C.ICompiler {
574
637
 
575
638
  private _compileModifierOR(ctx: I.IContext, rules: any[]): string {
576
639
 
577
- let result: string[] = [];
640
+ const result: string[] = [];
578
641
 
579
642
  for (const r of rules) {
580
643
 
@@ -595,7 +658,7 @@ implements C.ICompiler {
595
658
  );
596
659
  }
597
660
 
598
- private _compileModifierLIST(ctx: I.IContext, rules: any[]): string {
661
+ private _compileModifierLIST(ctx: I.IContext, rules: any[], traceOffset: number = 0): string {
599
662
 
600
663
  const result: string[] = [];
601
664
 
@@ -613,6 +676,10 @@ implements C.ICompiler {
613
676
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
614
677
 
615
678
  ctx.vName = this._lang.varName(ctx.vCursor++);
679
+ const vIter = this._lang.varName(ctx.vCursor++);
680
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.numberTemplateVar(
681
+ traceOffset ? this._lang.add(traceOffset, vIter) : vIter
682
+ )}]`;
616
683
 
617
684
  if (rules[0] !== B.ANY) {
618
685
 
@@ -621,9 +688,9 @@ implements C.ICompiler {
621
688
  [CLOSURE_ARG],
622
689
  this._lang.series([
623
690
  this._lang.forEach(
624
- CLOSURE_PARAM, ctx.vName, this._lang.ifThen(
691
+ CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(
625
692
  this._lang.not(this._compile(ctx, rules)),
626
- this._lang.returnValue(this._lang.literal(false))
693
+ this._lang.returnValue(this._addTrace(ctx))
627
694
  )
628
695
  ),
629
696
  this._lang.returnValue(this._lang.literal(true))
@@ -636,7 +703,7 @@ implements C.ICompiler {
636
703
  return this._lang.and(result);
637
704
  }
638
705
 
639
- private _compileModifierARRAY(ctx: I.IContext, rules: any[]): string {
706
+ private _compileModifierARRAY(ctx: I.IContext, rules: any[], traceOffset: number = 0): string {
640
707
 
641
708
  let a: number = 0;
642
709
  let b: number = -1;
@@ -716,6 +783,10 @@ implements C.ICompiler {
716
783
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
717
784
 
718
785
  ctx.vName = this._lang.varName(ctx.vCursor++);
786
+ const vIter = this._lang.varName(ctx.vCursor++);
787
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.numberTemplateVar(
788
+ traceOffset ? this._lang.add(traceOffset, vIter) : vIter
789
+ )}]`;
719
790
 
720
791
  switch (b) {
721
792
  case -1: {
@@ -749,9 +820,9 @@ implements C.ICompiler {
749
820
  [CLOSURE_ARG],
750
821
  this._lang.series([
751
822
  this._lang.forEach(
752
- CLOSURE_PARAM, ctx.vName, this._lang.ifThen(
823
+ CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(
753
824
  this._lang.not(this._compile(ctx, rules.slice(1))),
754
- this._lang.returnValue(this._lang.literal(false))
825
+ this._lang.returnValue(this._addTrace(ctx))
755
826
  )
756
827
  ),
757
828
  this._lang.returnValue(this._lang.literal(true))
@@ -781,6 +852,7 @@ implements C.ICompiler {
781
852
 
782
853
  const types = rules.slice();
783
854
  let tupleLength = 0;
855
+ let tupleLengthMin = 0;
784
856
 
785
857
  while (1) {
786
858
 
@@ -791,18 +863,18 @@ implements C.ICompiler {
791
863
  break;
792
864
  }
793
865
 
794
- if (typeof type === "string" && type.startsWith("...")) {
866
+ if (typeof type === 'string' && type.startsWith('...')) {
795
867
 
796
868
  throw new TypeError(`Invalid syntax for tuple: ${JSON.stringify(rules)}`);
797
869
  }
798
870
 
799
- if (typeof types[0] === "string" && types[0].startsWith("...")) {
871
+ if (typeof types[0] === 'string' && types[0].startsWith('...')) {
800
872
 
801
873
  ctx.trap();
802
874
 
803
875
  dots = types.shift();
804
876
 
805
- if (dots === "...") {
877
+ if (dots === '...') {
806
878
 
807
879
  /**
808
880
  * No more elements because "..." means all rest elements.
@@ -814,13 +886,14 @@ implements C.ICompiler {
814
886
 
815
887
  ctx.vName = this._lang.arraySlice(ctx.vName, i);
816
888
 
817
- if (type !== "any") {
889
+ if (type !== 'any') {
818
890
 
819
891
  result.push(this._compileModifierLIST(
820
- ctx, type
892
+ ctx, type, i
821
893
  ));
822
894
  }
823
895
 
896
+ tupleLengthMin = tupleLength;
824
897
  tupleLength = -1;
825
898
  }
826
899
  else if (!/^\d+$/.test(dots.slice(3))) {
@@ -831,22 +904,27 @@ implements C.ICompiler {
831
904
 
832
905
  const length = parseInt(dots.slice(3));
833
906
 
834
- if (length <= 3) {
907
+ if (length === 0) {
835
908
 
836
- const vName = ctx.vName;
909
+ throw new TypeError(`Invalid syntax for tuple: ${dots}`);
910
+ }
911
+ else if (length === 1) {
837
912
 
838
- for (let j = 0; j < length; j++) {
913
+ const vName = ctx.vName;
839
914
 
840
- ctx.vName = this._lang.arrayIndex(vName, i++);
841
- result.push(this._compile(ctx, type));
842
- }
915
+ ctx.tracePath = `${ctx.tracePath}[${i}]`;
916
+ ctx.vName = this._lang.arrayIndex(vName, i++);
917
+ result.push(this._addTraceOr(
918
+ ctx,
919
+ this._compile(ctx, type),
920
+ ));
843
921
  }
844
922
  else {
845
923
 
846
924
  ctx.vName = this._lang.arraySlice(ctx.vName, i, i + length);
847
925
 
848
926
  result.push(this._compileModifierARRAY(
849
- ctx, [length, type]
927
+ ctx, [length, type], i
850
928
  ));
851
929
 
852
930
  i += length;
@@ -859,8 +937,12 @@ implements C.ICompiler {
859
937
 
860
938
  ctx.trap(true);
861
939
 
940
+ ctx.tracePath = `${ctx.tracePath}[${i}]`;
862
941
  ctx.vName = this._lang.arrayIndex(ctx.vName, i++);
863
- result.push(this._compile(ctx, type));
942
+ result.push(this._addTraceOr(
943
+ ctx,
944
+ this._compile(ctx, type),
945
+ ));
864
946
  tupleLength++;
865
947
  }
866
948
 
@@ -869,9 +951,24 @@ implements C.ICompiler {
869
951
 
870
952
  if (tupleLength >= 0) {
871
953
 
872
- result.splice(1, -1, this._lang.eq(
873
- this._lang.arrayLength(ctx.vName),
874
- tupleLength
954
+ result.splice(1, -1, this._addTraceOr(
955
+ ctx,
956
+ this._lang.eq(
957
+ this._lang.arrayLength(ctx.vName),
958
+ tupleLength
959
+ ),
960
+ '.length'
961
+ ));
962
+ }
963
+ else if (tupleLengthMin >= 0) {
964
+
965
+ result.splice(1, -1, this._addTraceOr(
966
+ ctx,
967
+ this._lang.gte(
968
+ this._lang.arrayLength(ctx.vName),
969
+ tupleLengthMin
970
+ ),
971
+ '.length'
875
972
  ));
876
973
  }
877
974
 
@@ -880,7 +977,7 @@ implements C.ICompiler {
880
977
 
881
978
  private _validateTypeName(name: unknown): void {
882
979
 
883
- if (typeof name !== "string" || !/^\w+$/.test(name)) {
980
+ if (typeof name !== 'string' || !I.RE_VALID_CUSTOM_TYPE_NAME.test(name)) {
884
981
 
885
982
  throw new TypeError(`Invalid name ${
886
983
  JSON.stringify(name)
@@ -898,7 +995,8 @@ implements C.ICompiler {
898
995
  }
899
996
 
900
997
  this._defTypes[rules[0]] = this.compile({
901
- rule: rules.slice(1)
998
+ rule: rules.slice(1),
999
+ traceErrors: !!ctx.vTraceName
902
1000
  });
903
1001
 
904
1002
  return this._usePredefinedType(ctx, rules[0]);
@@ -949,24 +1047,25 @@ implements C.ICompiler {
949
1047
 
950
1048
  ctx.trap(true);
951
1049
 
952
- const CLOSURE_ARG = ctx.vName;
1050
+ const vCArg = ctx.vName;
953
1051
 
954
- const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
1052
+ const vCParam = this._lang.varName(ctx.vCursor++);
955
1053
 
956
- const FOR_IN_KEY = this._lang.varName(ctx.vCursor++);
1054
+ const vKey = this._lang.varName(ctx.vCursor++);
957
1055
 
958
1056
  ctx.vName = this._lang.varName(ctx.vCursor++);
1057
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.stringTemplateVar(vKey)}]`;
959
1058
 
960
1059
  const result = this._lang.and([
961
- this._lang.isStrucutre(CLOSURE_ARG, true),
1060
+ this._lang.isStrucutre(vCArg, true),
962
1061
  this._lang.closure(
963
- [CLOSURE_PARAM],
964
- [CLOSURE_ARG],
1062
+ [vCParam],
1063
+ [vCArg],
965
1064
  this._lang.series([
966
1065
  this._lang.forIn(
967
- CLOSURE_PARAM, FOR_IN_KEY, ctx.vName, this._lang.ifThen(
1066
+ vCParam, vKey, ctx.vName, this._lang.ifThen(
968
1067
  this._lang.not(this._compile(ctx, rules)),
969
- this._lang.returnValue(this._lang.literal(false))
1068
+ this._lang.returnValue(this._addTrace(ctx))
970
1069
  )
971
1070
  ),
972
1071
  this._lang.returnValue(this._lang.literal(true))
@@ -986,7 +1085,7 @@ implements C.ICompiler {
986
1085
  throw new SyntaxError(`Invalid dict ${JSON.stringify(rules)}.`);
987
1086
  }
988
1087
 
989
- let tmp: Record<string, string> = {};
1088
+ const tmp: Record<string, string> = {};
990
1089
 
991
1090
  const id = `${Date.now()}${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
992
1091
 
@@ -997,9 +1096,9 @@ implements C.ICompiler {
997
1096
 
998
1097
  const type = `${I.PREDEF_TYPE_SYMBOL}${this._lang.varName(id)}`;
999
1098
 
1000
- for (let key of rules[0]) {
1099
+ for (const key of rules[0]) {
1001
1100
 
1002
- if (typeof key !== "string") {
1101
+ if (typeof key !== 'string') {
1003
1102
 
1004
1103
  throw new SyntaxError(`Invalid key ${JSON.stringify(key)} for dict.`);
1005
1104
  }
@@ -1018,7 +1117,11 @@ implements C.ICompiler {
1018
1117
  const strict = !!ctx.flags[I.EFlags.STRICT];
1019
1118
 
1020
1119
  const result: string[] = [
1021
- this._lang.isStrucutre(ctx.vName, true)
1120
+ this._addTraceOr(
1121
+ ctx,
1122
+ this._lang.isStrucutre(ctx.vName, true),
1123
+ '!object'
1124
+ )
1022
1125
  ];
1023
1126
 
1024
1127
  const keys: string[] = [];
@@ -1059,7 +1162,7 @@ implements C.ICompiler {
1059
1162
  }
1060
1163
  else {
1061
1164
 
1062
- const matchResult = k.match(I.KEY_ARRAY_SUFFIX);
1165
+ const matchResult = I.KEY_ARRAY_SUFFIX.exec(k);
1063
1166
 
1064
1167
  if (matchResult) {
1065
1168
 
@@ -1102,8 +1205,12 @@ implements C.ICompiler {
1102
1205
  keys.push(k);
1103
1206
 
1104
1207
  ctx.vName = this._lang.fieldIndex(ctx.vName, this._lang.literal(k));
1208
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.literal(k)}]`;
1105
1209
 
1106
- result.push(this._compile(ctx, rule));
1210
+ result.push(this._addTraceOr(
1211
+ ctx,
1212
+ this._compile(ctx, rule)
1213
+ ));
1107
1214
 
1108
1215
  ctx.untrap();
1109
1216
  }
@@ -1132,7 +1239,7 @@ implements C.ICompiler {
1132
1239
 
1133
1240
  if (mapSymbol.length > 1) {
1134
1241
 
1135
- throw new SyntaxError("Only one '$.map' is allowed as rest-mapping.");
1242
+ throw new SyntaxError('Only one \'$.map\' is allowed as rest-mapping.');
1136
1243
  }
1137
1244
  else if (mapSymbol.length === 0) {
1138
1245
 
@@ -1193,7 +1300,7 @@ implements C.ICompiler {
1193
1300
  }
1194
1301
  else {
1195
1302
 
1196
- const matchResult = k.match(I.KEY_ARRAY_SUFFIX);
1303
+ const matchResult = I.KEY_ARRAY_SUFFIX.exec(k);
1197
1304
 
1198
1305
  if (matchResult) {
1199
1306
 
@@ -1291,7 +1398,7 @@ implements C.ICompiler {
1291
1398
 
1292
1399
  return Array.isArray(rule) && (
1293
1400
  rule[0] === M.OR ||
1294
- typeof rule[0] !== "string" ||
1401
+ typeof rule[0] !== 'string' ||
1295
1402
  !rule[0].startsWith(I.MODIFIER_PREFIX)
1296
1403
  );
1297
1404
  }