@litert/typeguard 1.1.0 → 1.3.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 (63) hide show
  1. package/CHANGES.md +10 -0
  2. package/lib/BuiltInTypeCompiler.d.ts +2 -2
  3. package/lib/BuiltInTypeCompiler.d.ts.map +1 -1
  4. package/lib/BuiltInTypeCompiler.js +1 -1
  5. package/lib/BuiltInTypeCompiler.js.map +1 -1
  6. package/lib/BuiltInTypes.d.ts +1 -1
  7. package/lib/BuiltInTypes.js +1 -1
  8. package/lib/Common.d.ts +19 -2
  9. package/lib/Common.d.ts.map +1 -1
  10. package/lib/Common.js +1 -1
  11. package/lib/Compiler.d.ts +1 -1
  12. package/lib/Compiler.d.ts.map +1 -1
  13. package/lib/Compiler.js +93 -39
  14. package/lib/Compiler.js.map +1 -1
  15. package/lib/Context.d.ts +5 -4
  16. package/lib/Context.d.ts.map +1 -1
  17. package/lib/Context.js +9 -6
  18. package/lib/Context.js.map +1 -1
  19. package/lib/FilterCompiler.d.ts +3 -3
  20. package/lib/FilterCompiler.d.ts.map +1 -1
  21. package/lib/FilterCompiler.js +2 -2
  22. package/lib/FilterCompiler.js.map +1 -1
  23. package/lib/InlineCompiler.d.ts +10 -3
  24. package/lib/InlineCompiler.d.ts.map +1 -1
  25. package/lib/InlineCompiler.js +16 -5
  26. package/lib/InlineCompiler.js.map +1 -1
  27. package/lib/Internal.d.ts +6 -4
  28. package/lib/Internal.d.ts.map +1 -1
  29. package/lib/Internal.js +3 -2
  30. package/lib/Internal.js.map +1 -1
  31. package/lib/Modifiers.d.ts +1 -1
  32. package/lib/Modifiers.js +1 -1
  33. package/lib/index.d.ts +1 -1
  34. package/lib/index.js +6 -2
  35. package/lib/index.js.map +1 -1
  36. package/lib/langs/JavaScript.d.ts +1 -1
  37. package/lib/langs/JavaScript.d.ts.map +1 -1
  38. package/lib/langs/JavaScript.js +29 -4
  39. package/lib/langs/JavaScript.js.map +1 -1
  40. package/package.json +12 -12
  41. package/src/examples/quick-start.ts +118 -2
  42. package/src/lib/BuiltInTypeCompiler.ts +2 -2
  43. package/src/lib/BuiltInTypes.ts +1 -1
  44. package/src/lib/Common.ts +41 -2
  45. package/src/lib/Compiler.ts +155 -50
  46. package/src/lib/Context.ts +9 -11
  47. package/src/lib/FilterCompiler.ts +4 -4
  48. package/src/lib/InlineCompiler.ts +36 -10
  49. package/src/lib/Internal.ts +8 -4
  50. package/src/lib/Modifiers.ts +1 -1
  51. package/src/lib/index.ts +1 -1
  52. package/src/lib/langs/JavaScript.ts +58 -5
  53. package/src/test/00-all.ts +1 -1
  54. package/src/test/01-elemental-types.ts +2 -1
  55. package/src/test/02-array-and-list.ts +18 -18
  56. package/src/test/03-tuple.ts +1 -1
  57. package/src/test/04-from-string.ts +2 -1
  58. package/src/test/05-string-asserts.ts +1 -1
  59. package/src/test/06-structure.ts +26 -26
  60. package/src/test/07-modifiers.ts +71 -10
  61. package/src/test/08-map-and-dict.ts +20 -20
  62. package/src/test/09-exceptions.ts +4 -4
  63. package/src/test/abstracts.ts +45 -7
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2021 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.
@@ -22,15 +22,14 @@ import * as B from './BuiltInTypes';
22
22
  import { BuiltInTypeCompiler } from './BuiltInTypeCompiler';
23
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,12 +44,43 @@ 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
81
  this._lang.varName('entry'),
82
+ options.traceErrors ? this._lang.varName('failedAsserts') : '',
83
+ options.traceErrors ? this._lang.varName('tracePrefix') : '',
54
84
  this._lang.varName('types'),
55
85
  referredTypes
56
86
  );
@@ -71,12 +101,28 @@ implements C.ICompiler {
71
101
  source: '',
72
102
  arguments: [{
73
103
  'name': ctx.vName,
74
- 'type': 'unknown'
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
@@ -164,6 +210,9 @@ implements C.ICompiler {
164
210
  case 'undefined':
165
211
 
166
212
  return this._lang.isUndefined(ctx.vName, true);
213
+
214
+ default:
215
+ break;
167
216
  }
168
217
 
169
218
  throw new TypeError('Unknwn rules.');
@@ -197,53 +246,65 @@ implements C.ICompiler {
197
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
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,
@@ -324,7 +385,7 @@ implements C.ICompiler {
324
385
 
325
386
  const offset = assertRule ? assertRule[1].length + 2 : 2;
326
387
 
327
- switch ((assertRule?.[1]) ?? rule.substr(0, 2)) {
388
+ switch ((assertRule?.[1]) ?? rule.slice(0, 2)) {
328
389
  case '==':
329
390
  case 'equal':
330
391
 
@@ -576,7 +637,7 @@ implements C.ICompiler {
576
637
 
577
638
  private _compileModifierOR(ctx: I.IContext, rules: any[]): string {
578
639
 
579
- let result: string[] = [];
640
+ const result: string[] = [];
580
641
 
581
642
  for (const r of rules) {
582
643
 
@@ -597,7 +658,7 @@ implements C.ICompiler {
597
658
  );
598
659
  }
599
660
 
600
- private _compileModifierLIST(ctx: I.IContext, rules: any[]): string {
661
+ private _compileModifierLIST(ctx: I.IContext, rules: any[], traceOffset: number = 0): string {
601
662
 
602
663
  const result: string[] = [];
603
664
 
@@ -615,6 +676,10 @@ implements C.ICompiler {
615
676
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
616
677
 
617
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
+ )}]`;
618
683
 
619
684
  if (rules[0] !== B.ANY) {
620
685
 
@@ -623,9 +688,9 @@ implements C.ICompiler {
623
688
  [CLOSURE_ARG],
624
689
  this._lang.series([
625
690
  this._lang.forEach(
626
- CLOSURE_PARAM, ctx.vName, this._lang.ifThen(
691
+ CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(
627
692
  this._lang.not(this._compile(ctx, rules)),
628
- this._lang.returnValue(this._lang.literal(false))
693
+ this._lang.returnValue(this._addTrace(ctx))
629
694
  )
630
695
  ),
631
696
  this._lang.returnValue(this._lang.literal(true))
@@ -638,7 +703,7 @@ implements C.ICompiler {
638
703
  return this._lang.and(result);
639
704
  }
640
705
 
641
- private _compileModifierARRAY(ctx: I.IContext, rules: any[]): string {
706
+ private _compileModifierARRAY(ctx: I.IContext, rules: any[], traceOffset: number = 0): string {
642
707
 
643
708
  let a: number = 0;
644
709
  let b: number = -1;
@@ -718,6 +783,10 @@ implements C.ICompiler {
718
783
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
719
784
 
720
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
+ )}]`;
721
790
 
722
791
  switch (b) {
723
792
  case -1: {
@@ -751,9 +820,9 @@ implements C.ICompiler {
751
820
  [CLOSURE_ARG],
752
821
  this._lang.series([
753
822
  this._lang.forEach(
754
- CLOSURE_PARAM, ctx.vName, this._lang.ifThen(
823
+ CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(
755
824
  this._lang.not(this._compile(ctx, rules.slice(1))),
756
- this._lang.returnValue(this._lang.literal(false))
825
+ this._lang.returnValue(this._addTrace(ctx))
757
826
  )
758
827
  ),
759
828
  this._lang.returnValue(this._lang.literal(true))
@@ -783,6 +852,7 @@ implements C.ICompiler {
783
852
 
784
853
  const types = rules.slice();
785
854
  let tupleLength = 0;
855
+ let tupleLengthMin = 0;
786
856
 
787
857
  while (1) {
788
858
 
@@ -819,10 +889,11 @@ implements C.ICompiler {
819
889
  if (type !== 'any') {
820
890
 
821
891
  result.push(this._compileModifierLIST(
822
- ctx, type
892
+ ctx, type, i
823
893
  ));
824
894
  }
825
895
 
896
+ tupleLengthMin = tupleLength;
826
897
  tupleLength = -1;
827
898
  }
828
899
  else if (!/^\d+$/.test(dots.slice(3))) {
@@ -833,22 +904,27 @@ implements C.ICompiler {
833
904
 
834
905
  const length = parseInt(dots.slice(3));
835
906
 
836
- if (length <= 3) {
907
+ if (length === 0) {
837
908
 
838
- const vName = ctx.vName;
909
+ throw new TypeError(`Invalid syntax for tuple: ${dots}`);
910
+ }
911
+ else if (length === 1) {
839
912
 
840
- for (let j = 0; j < length; j++) {
913
+ const vName = ctx.vName;
841
914
 
842
- ctx.vName = this._lang.arrayIndex(vName, i++);
843
- result.push(this._compile(ctx, type));
844
- }
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
+ ));
845
921
  }
846
922
  else {
847
923
 
848
924
  ctx.vName = this._lang.arraySlice(ctx.vName, i, i + length);
849
925
 
850
926
  result.push(this._compileModifierARRAY(
851
- ctx, [length, type]
927
+ ctx, [length, type], i
852
928
  ));
853
929
 
854
930
  i += length;
@@ -861,8 +937,12 @@ implements C.ICompiler {
861
937
 
862
938
  ctx.trap(true);
863
939
 
940
+ ctx.tracePath = `${ctx.tracePath}[${i}]`;
864
941
  ctx.vName = this._lang.arrayIndex(ctx.vName, i++);
865
- result.push(this._compile(ctx, type));
942
+ result.push(this._addTraceOr(
943
+ ctx,
944
+ this._compile(ctx, type),
945
+ ));
866
946
  tupleLength++;
867
947
  }
868
948
 
@@ -871,9 +951,24 @@ implements C.ICompiler {
871
951
 
872
952
  if (tupleLength >= 0) {
873
953
 
874
- result.splice(1, -1, this._lang.eq(
875
- this._lang.arrayLength(ctx.vName),
876
- 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'
877
972
  ));
878
973
  }
879
974
 
@@ -882,7 +977,7 @@ implements C.ICompiler {
882
977
 
883
978
  private _validateTypeName(name: unknown): void {
884
979
 
885
- if (typeof name !== 'string' || !/^[-:.\w]+$/.test(name)) {
980
+ if (typeof name !== 'string' || !I.RE_VALID_CUSTOM_TYPE_NAME.test(name)) {
886
981
 
887
982
  throw new TypeError(`Invalid name ${
888
983
  JSON.stringify(name)
@@ -900,7 +995,8 @@ implements C.ICompiler {
900
995
  }
901
996
 
902
997
  this._defTypes[rules[0]] = this.compile({
903
- rule: rules.slice(1)
998
+ rule: rules.slice(1),
999
+ traceErrors: !!ctx.vTraceName
904
1000
  });
905
1001
 
906
1002
  return this._usePredefinedType(ctx, rules[0]);
@@ -951,24 +1047,25 @@ implements C.ICompiler {
951
1047
 
952
1048
  ctx.trap(true);
953
1049
 
954
- const CLOSURE_ARG = ctx.vName;
1050
+ const vCArg = ctx.vName;
955
1051
 
956
- const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
1052
+ const vCParam = this._lang.varName(ctx.vCursor++);
957
1053
 
958
- const FOR_IN_KEY = this._lang.varName(ctx.vCursor++);
1054
+ const vKey = this._lang.varName(ctx.vCursor++);
959
1055
 
960
1056
  ctx.vName = this._lang.varName(ctx.vCursor++);
1057
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.stringTemplateVar(vKey)}]`;
961
1058
 
962
1059
  const result = this._lang.and([
963
- this._lang.isStrucutre(CLOSURE_ARG, true),
1060
+ this._lang.isStrucutre(vCArg, true),
964
1061
  this._lang.closure(
965
- [CLOSURE_PARAM],
966
- [CLOSURE_ARG],
1062
+ [vCParam],
1063
+ [vCArg],
967
1064
  this._lang.series([
968
1065
  this._lang.forIn(
969
- CLOSURE_PARAM, FOR_IN_KEY, ctx.vName, this._lang.ifThen(
1066
+ vCParam, vKey, ctx.vName, this._lang.ifThen(
970
1067
  this._lang.not(this._compile(ctx, rules)),
971
- this._lang.returnValue(this._lang.literal(false))
1068
+ this._lang.returnValue(this._addTrace(ctx))
972
1069
  )
973
1070
  ),
974
1071
  this._lang.returnValue(this._lang.literal(true))
@@ -988,7 +1085,7 @@ implements C.ICompiler {
988
1085
  throw new SyntaxError(`Invalid dict ${JSON.stringify(rules)}.`);
989
1086
  }
990
1087
 
991
- let tmp: Record<string, string> = {};
1088
+ const tmp: Record<string, string> = {};
992
1089
 
993
1090
  const id = `${Date.now()}${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
994
1091
 
@@ -999,7 +1096,7 @@ implements C.ICompiler {
999
1096
 
1000
1097
  const type = `${I.PREDEF_TYPE_SYMBOL}${this._lang.varName(id)}`;
1001
1098
 
1002
- for (let key of rules[0]) {
1099
+ for (const key of rules[0]) {
1003
1100
 
1004
1101
  if (typeof key !== 'string') {
1005
1102
 
@@ -1020,7 +1117,11 @@ implements C.ICompiler {
1020
1117
  const strict = !!ctx.flags[I.EFlags.STRICT];
1021
1118
 
1022
1119
  const result: string[] = [
1023
- this._lang.isStrucutre(ctx.vName, true)
1120
+ this._addTraceOr(
1121
+ ctx,
1122
+ this._lang.isStrucutre(ctx.vName, true),
1123
+ '!object'
1124
+ )
1024
1125
  ];
1025
1126
 
1026
1127
  const keys: string[] = [];
@@ -1104,8 +1205,12 @@ implements C.ICompiler {
1104
1205
  keys.push(k);
1105
1206
 
1106
1207
  ctx.vName = this._lang.fieldIndex(ctx.vName, this._lang.literal(k));
1208
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.literal(k)}]`;
1107
1209
 
1108
- result.push(this._compile(ctx, rule));
1210
+ result.push(this._addTraceOr(
1211
+ ctx,
1212
+ this._compile(ctx, rule)
1213
+ ));
1109
1214
 
1110
1215
  ctx.untrap();
1111
1216
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2021 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.
@@ -18,18 +18,18 @@ import * as i from './Internal';
18
18
 
19
19
  export class Context implements i.IContext {
20
20
 
21
- public trace: boolean;
22
-
23
- public tracePoint: number;
24
-
25
21
  public vCursor: number;
26
22
 
27
23
  public stack: i.IContextData[];
28
24
 
29
25
  public flags: Record<string, i.EFlagValue>;
30
26
 
27
+ public tracePath: string = '';
28
+
31
29
  public constructor(
32
30
  public vName: string,
31
+ public vTraceName: string,
32
+ public vTracePrefix: string,
33
33
  public readonly typeSlotName: string,
34
34
  public readonly referredTypes: Record<string, boolean>
35
35
  ) {
@@ -38,17 +38,14 @@ export class Context implements i.IContext {
38
38
  this.vCursor = 0;
39
39
 
40
40
  this.flags = {};
41
-
42
- this.tracePoint = 0;
43
-
44
- this.trace = false;
45
41
  }
46
42
 
47
43
  public trap(subjChanged: boolean = false): void {
48
44
 
49
45
  this.stack.push({
50
46
  vName: this.vName,
51
- flags: this.flags
47
+ flags: this.flags,
48
+ tracePath: this.tracePath,
52
49
  });
53
50
 
54
51
  const prevFlags = this.flags;
@@ -83,7 +80,8 @@ export class Context implements i.IContext {
83
80
  throw new Error('Failed to pop stack.');
84
81
  }
85
82
 
86
- this.vName = prev.vName;
87
83
  this.flags = prev.flags;
84
+ this.vName = prev.vName;
85
+ this.tracePath = prev.tracePath;
88
86
  }
89
87
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2021 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.
@@ -21,8 +21,8 @@ export class FilterCompiler
21
21
  implements I.IFilterCompiler {
22
22
 
23
23
  public constructor(
24
- private _lang: C.ILanguageBuilder,
25
- private _bitc: I.IBuiltInTypeCompiler
24
+ private readonly _lang: C.ILanguageBuilder,
25
+ private readonly _bitc: I.IBuiltInTypeCompiler
26
26
  ) {}
27
27
 
28
28
  public compile(rule: string, ctx: I.IContext): string {
@@ -36,7 +36,7 @@ implements I.IFilterCompiler {
36
36
  throw new TypeError('Only number is allowed as filter arguments.');
37
37
  }
38
38
 
39
- let ret: string[] = [];
39
+ const ret: string[] = [];
40
40
 
41
41
  switch (filter[0]) {
42
42
  case 'array.length':
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2021 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.
@@ -17,6 +17,7 @@
17
17
  import { createCompiler } from './Compiler';
18
18
  import { createJavaScriptLanguageBuilder } from './langs/JavaScript';
19
19
  import * as C from './Common';
20
+ import * as I from './Internal';
20
21
 
21
22
  export interface IInlineCompileOptions extends C.ICompileOptions {
22
23
 
@@ -34,14 +35,22 @@ export interface IInlineCompiler {
34
35
  *
35
36
  * @param options The options of compilation.
36
37
  */
37
- compile<T>(options: IInlineCompileOptions): C.TypeChecker<T>;
38
+ compile<T>(options: IInlineCompileOptions): C.ITypeChecker<T>;
38
39
 
39
40
  /**
40
41
  * Get the type-checker of a pre-defined type.
41
42
  *
42
43
  * @param name The name of the pre-defined type.
43
44
  */
44
- getPredefinedType<T>(name: string): C.TypeChecker<T>;
45
+ getPredefinedType<T>(name: string): C.ITypeChecker<T>;
46
+
47
+ /**
48
+ * Register a pre-defined type checker.
49
+ *
50
+ * @param name The name of the pre-defined type (without prefix `@`)
51
+ * @param checker The checker callback of the pre-defined type.
52
+ */
53
+ addPredefinedType<T>(name: string, checker: C.ITypeChecker<T>): this;
45
54
 
46
55
  /**
47
56
  * Check if a pre-defined type is compiled.
@@ -59,11 +68,11 @@ export interface IInlineCompiler {
59
68
  class InlineCompiler
60
69
  implements IInlineCompiler {
61
70
 
62
- private _defTypes: Record<string, C.TypeChecker<any>>;
71
+ private _defTypes: Record<string, C.ITypeChecker<any>>;
63
72
 
64
73
  private _missingTypes: Record<string, boolean>;
65
74
 
66
- private _compiler: C.ICompiler;
75
+ private readonly _compiler: C.ICompiler;
67
76
 
68
77
  public constructor() {
69
78
 
@@ -78,7 +87,7 @@ implements IInlineCompiler {
78
87
  );
79
88
  }
80
89
 
81
- public compile<T>(options: IInlineCompileOptions): C.TypeChecker<T> {
90
+ public compile<T>(options: IInlineCompileOptions): C.ITypeChecker<T> {
82
91
 
83
92
  const result = this._compiler.compile(options);
84
93
 
@@ -92,7 +101,7 @@ implements IInlineCompiler {
92
101
  stopOnEntry?: boolean
93
102
  ): void {
94
103
 
95
- for (let x of types) {
104
+ for (const x of types) {
96
105
 
97
106
  if (this._defTypes[x]) {
98
107
 
@@ -115,6 +124,18 @@ implements IInlineCompiler {
115
124
  }
116
125
  }
117
126
 
127
+ public addPredefinedType(name: string, checker: C.ITypeChecker<any>): this {
128
+
129
+ if (!I.RE_VALID_CUSTOM_TYPE_NAME.test(name)) {
130
+
131
+ throw new TypeError(`Invalid name ${ JSON.stringify(name) } for a pre-defined type.`);
132
+ }
133
+
134
+ this._defTypes[name] = checker;
135
+
136
+ return this;
137
+ }
138
+
118
139
  public detectUndefinedTypes(): string[] {
119
140
 
120
141
  return Object.keys(this._missingTypes);
@@ -125,7 +146,7 @@ implements IInlineCompiler {
125
146
  return !!this._defTypes[name];
126
147
  }
127
148
 
128
- public getPredefinedType(name: string): C.TypeChecker<any> {
149
+ public getPredefinedType(name: string): C.ITypeChecker<any> {
129
150
 
130
151
  if (!this._defTypes[name]) {
131
152
 
@@ -138,13 +159,18 @@ implements IInlineCompiler {
138
159
  private _wrapCheckerCode(
139
160
  info: C.ICompileResult,
140
161
  stopOnEntry: boolean = false
141
- ): C.TypeChecker<any> {
162
+ ): C.ITypeChecker<any> {
142
163
 
143
164
  const soe = stopOnEntry ? 'debugger;' : '';
144
165
 
145
166
  return (new Function(
146
167
  info.typeSlotName,
147
- `return function(${info.arguments[0].name}) {${soe}
168
+ `return function(${
169
+ info.arguments
170
+ .map((a) => a.initial ? `${a.name} = ${a.initial}` : a.name)
171
+ .join(',')
172
+ }) {
173
+ ${soe}
148
174
 
149
175
  return ${info.source};
150
176
  };`
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2021 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.
@@ -72,16 +72,18 @@ export enum EFlagValue {
72
72
 
73
73
  export interface IContextData {
74
74
 
75
+ flags: Record<string, EFlagValue>;
76
+
75
77
  vName: string;
76
78
 
77
- flags: Record<string, EFlagValue>;
79
+ tracePath: string;
78
80
  }
79
81
 
80
82
  export interface IContext extends IContextData {
81
83
 
82
- trace: boolean;
84
+ vTraceName: string;
83
85
 
84
- tracePoint: number;
86
+ vTracePrefix: string;
85
87
 
86
88
  vCursor: number;
87
89
 
@@ -123,3 +125,5 @@ export interface IBuiltInTypeCompiler {
123
125
 
124
126
  isElemental(type: string): boolean;
125
127
  }
128
+
129
+ export const RE_VALID_CUSTOM_TYPE_NAME = /^[-:.\w]+$/;