@cloudpss/expression 0.6.0-alpha.9 → 0.6.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 (157) hide show
  1. package/dist/analyze.d.ts +6 -3
  2. package/dist/analyze.d.ts.map +1 -1
  3. package/dist/analyze.js +13 -33
  4. package/dist/analyze.js.map +1 -1
  5. package/dist/definitions/argument.d.ts +3 -11
  6. package/dist/definitions/argument.d.ts.map +1 -1
  7. package/dist/definitions/constraint.d.ts +2 -2
  8. package/dist/definitions/constraint.d.ts.map +1 -1
  9. package/dist/definitions/constraint.js +1 -1
  10. package/dist/definitions/constraint.js.map +1 -1
  11. package/dist/definitions/parameter-group.js +4 -4
  12. package/dist/definitions/parameter-group.js.map +1 -1
  13. package/dist/definitions/parameter.d.ts +22 -14
  14. package/dist/definitions/parameter.d.ts.map +1 -1
  15. package/dist/definitions/parameter.js +10 -1
  16. package/dist/definitions/parameter.js.map +1 -1
  17. package/dist/definitions/utils.d.ts +28 -0
  18. package/dist/definitions/utils.d.ts.map +1 -0
  19. package/dist/definitions/utils.js +272 -0
  20. package/dist/definitions/utils.js.map +1 -0
  21. package/dist/definitions/variable.js +1 -1
  22. package/dist/definitions/variable.js.map +1 -1
  23. package/dist/definitions.d.ts +1 -2
  24. package/dist/definitions.d.ts.map +1 -1
  25. package/dist/definitions.js +1 -1
  26. package/dist/definitions.js.map +1 -1
  27. package/dist/eval.d.ts +3 -5
  28. package/dist/eval.d.ts.map +1 -1
  29. package/dist/eval.js +12 -20
  30. package/dist/eval.js.map +1 -1
  31. package/dist/expression.d.ts +10 -4
  32. package/dist/expression.d.ts.map +1 -1
  33. package/dist/expression.js +6 -6
  34. package/dist/expression.js.map +1 -1
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/interface.d.ts +30 -0
  40. package/dist/interface.d.ts.map +1 -0
  41. package/dist/interface.js +6 -0
  42. package/dist/interface.js.map +1 -0
  43. package/dist/main.d.ts +41 -28
  44. package/dist/main.d.ts.map +1 -1
  45. package/dist/main.js +165 -149
  46. package/dist/main.js.map +1 -1
  47. package/dist/migrate.d.ts +1 -1
  48. package/dist/migrate.d.ts.map +1 -1
  49. package/dist/migrate.js +25 -4
  50. package/dist/migrate.js.map +1 -1
  51. package/dist/migrator/access.d.ts.map +1 -1
  52. package/dist/migrator/access.js +64 -29
  53. package/dist/migrator/access.js.map +1 -1
  54. package/dist/migrator/call.d.ts.map +1 -1
  55. package/dist/migrator/call.js +298 -201
  56. package/dist/migrator/call.js.map +1 -1
  57. package/dist/migrator/concat.d.ts.map +1 -1
  58. package/dist/migrator/concat.js +15 -2
  59. package/dist/migrator/concat.js.map +1 -1
  60. package/dist/migrator/function.d.ts +6 -0
  61. package/dist/migrator/function.d.ts.map +1 -0
  62. package/dist/migrator/function.js +25 -0
  63. package/dist/migrator/function.js.map +1 -0
  64. package/dist/migrator/interface.d.ts +3 -1
  65. package/dist/migrator/interface.d.ts.map +1 -1
  66. package/dist/migrator/interface.js.map +1 -1
  67. package/dist/migrator/node.d.ts.map +1 -1
  68. package/dist/migrator/node.js +55 -7
  69. package/dist/migrator/node.js.map +1 -1
  70. package/dist/migrator/operator.d.ts.map +1 -1
  71. package/dist/migrator/operator.js +107 -60
  72. package/dist/migrator/operator.js.map +1 -1
  73. package/dist/migrator/serialize.d.ts +4 -0
  74. package/dist/migrator/serialize.d.ts.map +1 -0
  75. package/dist/migrator/serialize.js +21 -0
  76. package/dist/migrator/serialize.js.map +1 -0
  77. package/dist/migrator/special.d.ts.map +1 -1
  78. package/dist/migrator/special.js +31 -0
  79. package/dist/migrator/special.js.map +1 -1
  80. package/dist/migrator/state.d.ts +4 -4
  81. package/dist/migrator/state.d.ts.map +1 -1
  82. package/dist/migrator/state.js +29 -31
  83. package/dist/migrator/state.js.map +1 -1
  84. package/dist/migrator/symbol.d.ts.map +1 -1
  85. package/dist/migrator/symbol.js +34 -12
  86. package/dist/migrator/symbol.js.map +1 -1
  87. package/dist/migrator/to-type.d.ts.map +1 -1
  88. package/dist/migrator/to-type.js +21 -4
  89. package/dist/migrator/to-type.js.map +1 -1
  90. package/dist/migrator/utils.d.ts +6 -0
  91. package/dist/migrator/utils.d.ts.map +1 -1
  92. package/dist/migrator/utils.js +48 -1
  93. package/dist/migrator/utils.js.map +1 -1
  94. package/dist/parser.d.ts +2 -2
  95. package/dist/parser.d.ts.map +1 -1
  96. package/dist/parser.js +27 -8
  97. package/dist/parser.js.map +1 -1
  98. package/dist/re-exports.d.ts +4 -0
  99. package/dist/re-exports.d.ts.map +1 -0
  100. package/dist/re-exports.js +3 -0
  101. package/dist/re-exports.js.map +1 -0
  102. package/dist/scope.d.ts +16 -17
  103. package/dist/scope.d.ts.map +1 -1
  104. package/dist/scope.js +127 -66
  105. package/dist/scope.js.map +1 -1
  106. package/dist/type.d.ts +22 -10
  107. package/dist/type.d.ts.map +1 -1
  108. package/dist/type.js +21 -24
  109. package/dist/type.js.map +1 -1
  110. package/package.json +8 -5
  111. package/src/analyze.ts +20 -39
  112. package/src/definitions/argument.ts +3 -13
  113. package/src/definitions/constraint.ts +3 -3
  114. package/src/definitions/parameter-group.ts +4 -4
  115. package/src/definitions/parameter.ts +47 -24
  116. package/src/definitions/utils.ts +288 -0
  117. package/src/definitions/variable.ts +1 -1
  118. package/src/definitions.ts +1 -28
  119. package/src/eval.ts +16 -25
  120. package/src/expression.ts +16 -8
  121. package/src/index.ts +3 -1
  122. package/src/interface.ts +35 -0
  123. package/src/main.ts +232 -200
  124. package/src/migrate.ts +27 -4
  125. package/src/migrator/access.ts +67 -37
  126. package/src/migrator/call.ts +287 -190
  127. package/src/migrator/concat.ts +15 -2
  128. package/src/migrator/function.ts +27 -0
  129. package/src/migrator/interface.ts +3 -1
  130. package/src/migrator/node.ts +56 -6
  131. package/src/migrator/operator.ts +110 -64
  132. package/src/migrator/serialize.ts +21 -0
  133. package/src/migrator/special.ts +31 -0
  134. package/src/migrator/state.ts +31 -34
  135. package/src/migrator/symbol.ts +33 -12
  136. package/src/migrator/to-type.ts +23 -4
  137. package/src/migrator/utils.ts +48 -1
  138. package/src/parser.ts +33 -8
  139. package/src/re-exports.ts +47 -0
  140. package/src/scope.ts +146 -77
  141. package/src/type.ts +40 -25
  142. package/tests/analyze.ts +45 -6
  143. package/tests/compile.ts +65 -0
  144. package/tests/condition.ts +33 -17
  145. package/tests/definition.ts +237 -18
  146. package/tests/eval-complex.ts +79 -13
  147. package/tests/eval.ts +59 -12
  148. package/tests/import.ts +21 -7
  149. package/tests/main.ts +58 -0
  150. package/tests/migrate.ts +317 -0
  151. package/tests/scope.ts +3 -3
  152. package/tests/template.ts +36 -0
  153. package/dist/context.d.ts +0 -41
  154. package/dist/context.d.ts.map +0 -1
  155. package/dist/context.js +0 -18
  156. package/dist/context.js.map +0 -1
  157. package/src/context.ts +0 -54
@@ -1,42 +1,261 @@
1
+ import { jest } from '@jest/globals';
1
2
  import * as def from '../dist/definitions.js';
3
+ import { Expression } from '../dist/expression.js';
2
4
 
3
5
  describe('definitions', () => {
4
6
  it('should handle parameter', () => {
5
- expect(def.Parameter()).toBeDefined();
6
- expect(def.isParameter(def.Parameter())).toBe(true);
7
+ expect(def.isParameter(null)).toBe(false);
8
+ expect(def.isParameter(() => void 0)).toBe(false);
9
+ expect(def.isParameter({ key: '', name: '', description: '', type: 'real', value: 0 })).toBe(true);
10
+ expect(def.isParameter({ type: 'label' })).toBe(false);
11
+ expect(def.isParameter({ name: '', description: '', items: [] })).toBe(false);
7
12
  });
8
13
 
9
14
  it('should handle parameter group', () => {
10
- expect(def.ParameterGroup()).toBeDefined();
11
- expect(def.isParameterGroup(def.ParameterGroup())).toBe(true);
15
+ expect(def.isParameterGroup(null)).toBe(false);
16
+ expect(def.isParameterGroup(() => void 0)).toBe(false);
17
+ expect(def.isParameterGroup({ name: '', description: '', items: [] })).toBe(true);
18
+ expect(def.isParameterGroup({ items: [] })).toBe(false);
19
+ expect(def.isParameterGroup({ key: '', name: '', description: '', items: [] })).toBe(false);
12
20
  });
13
21
 
14
22
  it('should handle parameter decoration', () => {
15
- expect(def.ParameterDecoration()).toBeDefined();
16
- expect(def.isParameterDecoration(def.ParameterDecoration())).toBe(true);
23
+ expect(def.isParameterDecoration(null)).toBe(false);
24
+ expect(def.isParameterDecoration(() => void 0)).toBe(false);
25
+ expect(def.isParameterDecoration({ type: 'label' })).toBe(true);
26
+ expect(def.isParameterDecoration({ name: '', description: '', items: [] })).toBe(false);
27
+ expect(def.isParameterDecoration({ key: '', name: '', description: '', type: 'real', value: 0 })).toBe(false);
17
28
  });
18
29
 
19
30
  it('should handle parameter decoration group', () => {
20
- expect(def.ParameterDecorationGroup()).toBeDefined();
21
- expect(def.isParameterDecorationGroup(def.ParameterDecorationGroup())).toBe(true);
31
+ expect(def.isParameterDecorationGroup(null)).toBe(false);
32
+ expect(def.isParameterDecorationGroup(() => void 0)).toBe(false);
33
+ expect(def.isParameterDecorationGroup({ items: [] })).toBe(true);
34
+ expect(def.isParameterDecorationGroup({ name: '', description: '', items: [] })).toBe(false);
22
35
  });
23
36
 
24
37
  it('should handle variable', () => {
25
- expect(def.Variable()).toBeDefined();
26
- expect(def.isVariable(def.Variable())).toBe(true);
38
+ expect(def.isVariable(null)).toBe(false);
39
+ expect(def.isVariable(() => void 0)).toBe(false);
40
+ expect(def.isVariable({ key: '', value: 0 })).toBe(true);
41
+ expect(def.isVariable({ key: '', name: '', description: '', type: 'real', value: 0 })).toBe(false);
27
42
  });
28
43
 
29
44
  it('should handle choice', () => {
30
- expect(def.Choice()).toBeDefined();
31
- expect(def.isChoice(def.Choice())).toBe(true);
45
+ expect(def.isChoice(null)).toBe(false);
46
+ expect(def.isChoice(() => void 0)).toBe(false);
47
+ expect(def.isChoice({ name: '', description: '' })).toBe(true);
48
+ expect(def.isChoice({ key: null, name: '', description: '' })).toBe(true);
49
+ expect(def.isChoice({ key: '', name: '', description: '' })).toBe(true);
50
+ });
51
+
52
+ it('should handle constraint', () => {
53
+ expect(def.isConstraint(null)).toBe(false);
54
+ expect(def.isConstraint(() => void 0)).toBe(false);
55
+ expect(def.isConstraint({ name: '', description: '' })).toBe(false);
56
+ expect(def.isConstraint({ key: null, name: '', description: '' })).toBe(false);
57
+ expect(def.isConstraint({ key: '', name: '', description: '' })).toBe(false);
58
+ expect(def.isConstraint({ message: '' })).toBe(false);
59
+ expect(def.isConstraint({ condition: '' })).toBe(false);
60
+ expect(def.isConstraint({ message: '', condition: '' })).toBe(true);
61
+ expect(def.isConstraint({ message: '', condition: '', level: 'error' })).toBe(true);
62
+ expect(def.isConstraint({ message: '', condition: '', level: 'error', messageData: '' })).toBe(true);
63
+ expect(def.isConstraint({ message: '', condition: '', messageData: {} })).toBe(true);
64
+ expect(def.isConstraint({ message: '', condition: '', messageData: null })).toBe(true);
65
+ expect(def.isConstraint({ message: '', condition: '', messageData: undefined })).toBe(true);
66
+ expect(def.isConstraint({ message: '', condition: '', messageData: () => void 0 })).toBe(true);
67
+ expect(def.isConstraint({ message: '', condition: '', messageData: 1 })).toBe(false);
68
+ });
32
69
 
33
- expect(def.Choice('x').key).toBe('x');
34
- expect(def.isChoice(def.Choice('x'))).toBe(true);
70
+ it('should generate track id', () => {
71
+ const mockRandom = jest
72
+ .spyOn(Math, 'random')
73
+ .mockReturnValueOnce(0)
74
+ .mockReturnValueOnce(1 - 2 ** -53);
75
+ const id1 = def.generateTrackId();
76
+ const id2 = def.generateTrackId();
77
+ mockRandom.mockRestore();
78
+ expect(id1).toBe(1);
79
+ expect(id2).toBe(0x7fff_ffff);
80
+ });
35
81
 
36
- expect(def.Choice(0).key).toBe(0);
37
- expect(def.isChoice(def.Choice(0))).toBe(true);
82
+ it('should convert argument value', () => {
83
+ // @ts-expect-error 未知类型
84
+ expect(def.toArgumentValue(undefined, { type: 'xxx', key: '', name: '', description: '', value: 1 })).toBe(
85
+ null,
86
+ );
87
+ expect(def.toArgumentValue(undefined, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(
88
+ null,
89
+ );
90
+ expect(def.toArgumentValue(null, { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(null);
91
+ expect(def.toArgumentValue('12', { type: 'real', key: '', name: '', description: '', value: 1 })).toBe(12);
92
+ expect(def.toArgumentValue(null, { type: 'text', key: '', name: '', description: '', value: '1' })).toBe('');
93
+ expect(def.toArgumentValue(12, { type: 'text', key: '', name: '', description: '', value: '1' })).toBe('12');
94
+ expect(
95
+ def.toArgumentValue(null, { type: 'grouped', key: '', name: '', description: '', value: {}, items: [] }),
96
+ ).toBe(null);
38
97
 
39
- expect(def.Choice(false).key).toBe(false);
40
- expect(def.isChoice(def.Choice(false))).toBe(true);
98
+ expect(
99
+ def.toArgumentValue(null, { type: 'choice', key: '', name: '', description: '', value: 1, choices: [] }),
100
+ ).toBe(null);
101
+ expect(
102
+ def.toArgumentValue(null, {
103
+ type: 'choice',
104
+ key: '',
105
+ name: '',
106
+ description: '',
107
+ value: 1,
108
+ choices: [{ key: '0', name: '', description: '' }],
109
+ }),
110
+ ).toBe('');
111
+ expect(
112
+ def.toArgumentValue(null, {
113
+ type: 'choice',
114
+ key: '',
115
+ name: '',
116
+ description: '',
117
+ value: 1,
118
+ choices: [
119
+ { key: '0', name: '', description: '' },
120
+ { key: 1, name: '', description: '' },
121
+ ],
122
+ }),
123
+ ).toBe('');
124
+ expect(def.toArgumentValue(null, { type: 'logical', key: '', name: '', description: '', value: 1 })).toBe(null);
125
+ expect(def.toArgumentValue('0', { type: 'logical', key: '', name: '', description: '', value: 1 })).toBe(0);
126
+ expect(
127
+ def.toArgumentValue(null, {
128
+ type: 'logical',
129
+ key: '',
130
+ name: '',
131
+ description: '',
132
+ value: false,
133
+ choices: [
134
+ { key: 0, name: '', description: '' },
135
+ { key: 1, name: '', description: '' },
136
+ ],
137
+ }),
138
+ ).toBe(null);
139
+ expect(
140
+ def.toArgumentValue(null, {
141
+ type: 'logical',
142
+ key: '',
143
+ name: '',
144
+ description: '',
145
+ value: true,
146
+ }),
147
+ ).toBe(null);
148
+ expect(
149
+ def.toArgumentValue(null, {
150
+ type: 'logical',
151
+ key: '',
152
+ name: '',
153
+ description: '',
154
+ value: 'true',
155
+ }),
156
+ ).toBe('');
157
+ expect(
158
+ def.toArgumentValue(null, {
159
+ type: 'logical',
160
+ key: '',
161
+ name: '',
162
+ description: '',
163
+ value: 'true',
164
+ choices: [
165
+ { key: '0', name: '', description: '' },
166
+ { key: '1', name: '', description: '' },
167
+ ],
168
+ }),
169
+ ).toBe('');
170
+ expect(
171
+ def.toArgumentValue(null, {
172
+ type: 'multiSelect',
173
+ key: '',
174
+ name: '',
175
+ description: '',
176
+ value: [1],
177
+ choices: [],
178
+ }),
179
+ ).toStrictEqual([]);
180
+ expect(
181
+ def.toArgumentValue(1, {
182
+ type: 'multiSelect',
183
+ key: '',
184
+ name: '',
185
+ description: '',
186
+ value: [1],
187
+ choices: [],
188
+ }),
189
+ ).toStrictEqual([1]);
190
+ expect(
191
+ def.toArgumentValue('', {
192
+ type: 'multiSelect',
193
+ key: '',
194
+ name: '',
195
+ description: '',
196
+ value: [1],
197
+ choices: [],
198
+ }),
199
+ ).toStrictEqual(null);
200
+ expect(
201
+ def.toArgumentValue('', {
202
+ type: 'multiSelect',
203
+ key: '',
204
+ name: '',
205
+ description: '',
206
+ value: [1],
207
+ choices: Expression(''),
208
+ }),
209
+ ).toStrictEqual(null);
210
+ expect(
211
+ def.toArgumentValue(['', '1', ' 2 '], {
212
+ type: 'multiSelect',
213
+ key: '',
214
+ name: '',
215
+ description: '',
216
+ value: [1],
217
+ choices: [],
218
+ }),
219
+ ).toStrictEqual(null);
220
+ expect(
221
+ def.toArgumentValue(['0', '1', ' 2 '], {
222
+ type: 'multiSelect',
223
+ key: '',
224
+ name: '',
225
+ description: '',
226
+ value: [1],
227
+ choices: [],
228
+ }),
229
+ ).toStrictEqual([0, 1, 2]);
230
+ expect(
231
+ def.toArgumentValue(['0', '1', ' 2 '], {
232
+ type: 'multiSelect',
233
+ key: '',
234
+ name: '',
235
+ description: '',
236
+ value: [],
237
+ choices: [],
238
+ }),
239
+ ).toStrictEqual([0, 1, 2]);
240
+ expect(
241
+ def.toArgumentValue([1, 2], {
242
+ type: 'multiSelect',
243
+ key: '',
244
+ name: '',
245
+ description: '',
246
+ value: ['1'],
247
+ choices: [],
248
+ }),
249
+ ).toStrictEqual(['1', '2']);
250
+ expect(
251
+ def.toArgumentValue(['', '1', ' 2 '], {
252
+ type: 'multiSelect',
253
+ key: '',
254
+ name: '',
255
+ description: '',
256
+ value: [1],
257
+ choices: Expression(''),
258
+ }),
259
+ ).toStrictEqual(null);
41
260
  });
42
261
  });
@@ -1,5 +1,5 @@
1
1
  import { VmExtern } from '@mirascript/mirascript';
2
- import { Evaluator, Expression, Scope } from '../dist/index.js';
2
+ import { Evaluator, Expression, isExpression, Scope } from '../dist/index.js';
3
3
 
4
4
  const e = new Evaluator();
5
5
 
@@ -44,24 +44,23 @@ describe('Evaluator should work correctly', () => {
44
44
  const result = e.evaluate(Expression('a.0'), s);
45
45
  expect(result).toBe(36);
46
46
  });
47
- it('should eval deep with extern', () => {
47
+ it('should not eval deep with extern', () => {
48
48
  const s = new Scope(
49
49
  {
50
- a: new VmExtern([Expression('b.0 + d.0')]),
51
- b: new VmExtern([Expression('c.0 + d.0')]),
52
- c: new VmExtern([12]),
53
- d: new VmExtern([Expression('c.0')]),
50
+ a: new VmExtern([Expression('1')]),
54
51
  },
55
52
  false,
56
53
  );
57
- const result = e.evaluate(Expression('a.0'), s);
58
- expect(result).toBe(36);
54
+ const result = e.evaluate<VmExtern>(Expression('a.0'), s);
55
+ expect(result?.value).toStrictEqual(Expression('1'));
59
56
  });
60
57
  it('should eval deep with compiled expression', () => {
58
+ const b = e.compile(Expression('c+d'));
59
+ expect(isExpression(b)).toBe(true);
61
60
  const s = new Scope(
62
61
  {
63
62
  a: Expression('b+d'),
64
- b: e.compile(Expression('c+d')),
63
+ b: b,
65
64
  c: 12,
66
65
  d: Expression('c'),
67
66
  },
@@ -70,6 +69,62 @@ describe('Evaluator should work correctly', () => {
70
69
  const result = e.evaluate(Expression('a'), s);
71
70
  expect(result).toBe(36);
72
71
  });
72
+ it('should eval deep with object and array', () => {
73
+ const s = new Scope(
74
+ {
75
+ arr: [1, 2, Expression('arr.0 + obj.x'), Expression('arr.2 + obj.y')],
76
+ obj: { x: 3, y: Expression('arr.1 + obj.x') },
77
+ arr_copy: Expression('[..arr]'),
78
+ },
79
+ false,
80
+ );
81
+ const result = e.evaluate<{
82
+ arr: number[];
83
+ obj: Record<string, number>;
84
+ }>(Expression('(arr: [..arr], obj: (..obj))'), s)!;
85
+ expect(result).toStrictEqual({ arr: [1, 2, 4, 9], obj: { x: 3, y: 5 } });
86
+ expect(result).not.toBeProxy();
87
+ expect(result.arr).not.toBeProxy();
88
+ expect(result.obj).not.toBeProxy();
89
+ const copy = e.evaluate<number[]>(Expression('[..arr_copy]'), s);
90
+ expect(copy).toStrictEqual([1, 2, 4, 9]);
91
+ expect(copy).not.toBeProxy();
92
+ });
93
+ it('should eval with arr expr', () => {
94
+ const s = new Scope(
95
+ {
96
+ arr: Expression('[a0, 2, 3..5]'),
97
+ a0: Expression('0.4 + 0.6'),
98
+ start: Expression('0'),
99
+ end: Expression('start + 2'),
100
+ },
101
+ false,
102
+ );
103
+ const result = e.evaluate<number[]>(Expression('arr'), s);
104
+ expect(result).toStrictEqual([1, 2, 3, 4, 5]);
105
+ expect(result).not.toBeProxy();
106
+ const slice = e.evaluate<number[]>(Expression('arr[start..end]'), s);
107
+ expect(slice).toStrictEqual([1, 2, 3]);
108
+ expect(slice).not.toBeProxy();
109
+ const field = e.evaluate<number>(Expression('arr.0'), s);
110
+ expect(field).toBe(1);
111
+ });
112
+ it('should eval with obj expr', () => {
113
+ const s = new Scope(
114
+ {
115
+ o0: Expression('"s$(arr)"'),
116
+ arr: Expression('[1, 2, 3..5]'),
117
+ obj: Expression('(x: o0, y: 2 + arr.0, z: arr[1..arr.1 + 2])'),
118
+ },
119
+ false,
120
+ );
121
+ const result = e.evaluate<{ x: string; y: number; z: number[] }>(Expression('obj'), s)!;
122
+ expect(result).toStrictEqual({ x: 's1, 2, 3, 4, 5', y: 3, z: [2, 3, 4, 5] });
123
+ expect(result).not.toBeProxy();
124
+ expect(result.z).not.toBeProxy();
125
+ const field = e.evaluate<string>(Expression('obj.x'), s);
126
+ expect(field).toStrictEqual('s1, 2, 3, 4, 5');
127
+ });
73
128
  it('should eval complex', () => {
74
129
  const s = new Scope(
75
130
  {
@@ -105,7 +160,7 @@ describe('Evaluator should work correctly', () => {
105
160
  const result = e.evaluate(Expression('a + a + a + a + a + a + a + a + a + a'), s);
106
161
  expect(result).toBe(10);
107
162
  });
108
- it('should report loop', () => {
163
+ it('should report loop by scope', () => {
109
164
  const s = new Scope(
110
165
  {
111
166
  a: Expression('b'),
@@ -114,6 +169,17 @@ describe('Evaluator should work correctly', () => {
114
169
  },
115
170
  true,
116
171
  );
172
+ expect(() => e.evaluate(Expression('a'), s)).toThrow(/Execution recursion exceeds limit/);
173
+ });
174
+ it('should report loop by mirascript', () => {
175
+ const s = new Scope(
176
+ {
177
+ a: Expression('fn x{b}x()'),
178
+ b: Expression('fn x{c}x()'),
179
+ c: Expression('fn x{a}x()'),
180
+ },
181
+ true,
182
+ );
117
183
  expect(() => e.evaluate(Expression('a'), s)).toThrow(/Maximum call depth exceeded/);
118
184
  });
119
185
 
@@ -132,7 +198,7 @@ describe('Evaluator should work correctly', () => {
132
198
  a: new ArrayBuffer(0),
133
199
  v: new DataView(new ArrayBuffer(0)),
134
200
  test() {
135
- expect(this).toBeProxy();
201
+ expect(this).not.toBeProxy();
136
202
  expect(this.d).not.toBeProxy();
137
203
  expect(this.r).not.toBeProxy();
138
204
  // eslint-disable-next-line @typescript-eslint/unbound-method
@@ -144,7 +210,7 @@ describe('Evaluator should work correctly', () => {
144
210
  },
145
211
  });
146
212
  const s = new Scope({ o }, true);
147
- expect(e.evaluate(Expression('o.test()'), s)).toBe(3);
148
- expect(e.evaluate(Expression('o.z'), s)).toBe(3);
213
+ expect(e.evaluate<VmExtern>(Expression('o.test()'), s)?.value).toStrictEqual(Expression('o.y+o.x.p'));
214
+ expect(e.evaluate<VmExtern>(Expression('o.z'), s)?.value).toStrictEqual(Expression('o.y+o.x.p'));
149
215
  });
150
216
  });
package/tests/eval.ts CHANGED
@@ -1,9 +1,17 @@
1
- import { Evaluator, Expression, Scope } from '../dist/index.js';
1
+ import { Evaluator, Expression, Scope, VmFunction } from '../dist/index.js';
2
2
 
3
- const e = new Evaluator();
3
+ const e = new Evaluator({
4
+ logger: {
5
+ warn: () => void 0,
6
+ error: () => void 0,
7
+ info: () => void 0,
8
+ debug: () => void 0,
9
+ },
10
+ });
4
11
  const s = new Scope({}, false);
12
+ const st = new Scope(null, true);
5
13
 
6
- describe('Evaluator should work correctly', () => {
14
+ describe('Evaluator.evaluate should work correctly', () => {
7
15
  it('should eval const', () => {
8
16
  const result = e.evaluate(Expression('12'), s);
9
17
  expect(result).toBe(12);
@@ -16,11 +24,39 @@ describe('Evaluator should work correctly', () => {
16
24
 
17
25
  it('should eval error', () => {
18
26
  const result = e.evaluate(Expression('exp+++'), s);
19
- expect(result).toBe(undefined);
27
+ expect(result).toBe(null);
28
+ });
29
+
30
+ it('should eval null', () => {
31
+ const result = e.evaluate(null, s);
32
+ expect(result).toBe(null);
33
+ });
34
+ it('should eval undefined', () => {
35
+ const result = e.evaluate(undefined, s);
36
+ expect(result).toBe(null);
37
+ });
38
+ it('should eval value', () => {
39
+ {
40
+ const result = e.evaluate(1, s);
41
+ expect(result).toBe(1);
42
+ }
43
+ {
44
+ const result = e.evaluate('', s);
45
+ expect(result).toBe('');
46
+ }
47
+ {
48
+ const result = e.evaluate(false, s);
49
+ expect(result).toBe(false);
50
+ }
51
+ });
52
+ it('should eval vm function', () => {
53
+ const f = VmFunction(() => 1);
54
+ const result = e.evaluate(f, s);
55
+ expect(result).toBe(f);
20
56
  });
21
57
  });
22
58
 
23
- describe('Evaluator should convert result', () => {
59
+ describe('Evaluator.evaluate should convert result', () => {
24
60
  it('should eval const', () => {
25
61
  const result = e.evaluate(Expression('12', 'string'), s);
26
62
  expect(result).toBe('12');
@@ -31,12 +67,23 @@ describe('Evaluator should convert result', () => {
31
67
  expect(result).toBe(Math.exp(2).toString());
32
68
  });
33
69
 
34
- it('should not convert when error', () => {
35
- // syntax error
36
- const result = e.evaluate(Expression('exp+++', 's'), s, 1);
37
- expect(result).toBe(1);
38
- // runtime error
39
- const result2 = e.evaluate(Expression('x() + 1', 's'), s, 1);
40
- expect(result2).toBe(1);
70
+ it('should not convert defaults', () => {
71
+ {
72
+ const result = e.evaluate(Expression('nil', 's'), s, 1);
73
+ expect(result).toBe(1);
74
+ }
75
+ {
76
+ // syntax error
77
+ expect(() => e.evaluate(Expression('exp+++', 's'), undefined, 1)).toThrow();
78
+ }
79
+ {
80
+ // syntax error cached
81
+ expect(() => e.evaluate(Expression('exp+++', 's'), st)).toThrow();
82
+ }
83
+ {
84
+ // runtime error
85
+ const result = e.evaluate(Expression('x() + 1', 's'), s, 1);
86
+ expect(result).toBe(1);
87
+ }
41
88
  });
42
89
  });
package/tests/import.ts CHANGED
@@ -1,9 +1,23 @@
1
1
  import { VmFunction } from '@mirascript/mirascript';
2
2
  import { Evaluator, Expression, Scope } from '../dist/index.js';
3
- import { lib } from '@mirascript/mirascript/subtle';
3
+ import { convert, lib } from '@mirascript/mirascript/subtle';
4
4
 
5
- const e = new Evaluator();
6
- const e2 = new Evaluator();
5
+ const e = new Evaluator({
6
+ logger: {
7
+ warn: () => void 0,
8
+ error: () => void 0,
9
+ info: () => void 0,
10
+ debug: () => void 0,
11
+ },
12
+ });
13
+ const e2 = new Evaluator({
14
+ logger: {
15
+ warn: () => void 0,
16
+ error: () => void 0,
17
+ info: () => void 0,
18
+ debug: () => void 0,
19
+ },
20
+ });
7
21
  const s = new Scope(undefined, false);
8
22
 
9
23
  describe('Import should work correctly', () => {
@@ -11,21 +25,21 @@ describe('Import should work correctly', () => {
11
25
  e.import({ add: (x: number, y: number) => x + y });
12
26
  expect(e.evaluate(Expression('add(1,2)'), s)).toBe(3);
13
27
  expect(e.evaluate(Expression('add::type()'), s)).toBe('extern');
14
- expect(e2.evaluate(Expression('add(1,2)'), s)).toBe(undefined);
15
- expect(e2.evaluate(Expression('add::type()'), s)).toBe('nil');
28
+ expect(e2.evaluate(Expression('add(1,2)'), s)).toBe(null);
29
+ expect(e2.evaluate(Expression('add::type()'), s)).toBe(null);
16
30
  });
17
31
 
18
32
  it('can remove modules', () => {
19
33
  e.import({ add: (x: number, y: number) => x + y });
20
34
  expect(e.evaluate(Expression('add(1,2)'), s)).toBe(3);
21
35
  e.import({ add: undefined });
22
- expect(e.evaluate(Expression('add(1,2)'), s)).toBe(undefined);
36
+ expect(e.evaluate(Expression('add(1,2)'), s)).toBe(null);
23
37
  });
24
38
 
25
39
  it('can replace modules', () => {
26
40
  e.import({ add: (x: number, y: number) => x + y });
27
41
  expect(e.evaluate(Expression('add::type()'), s)).toBe('extern');
28
- e.import({ add: VmFunction((x, y) => lib.to_number(x) + lib.to_number(y)) });
42
+ e.import({ add: VmFunction((x, y) => convert.toNumber(x, 0) + convert.toNumber(y, 0)) });
29
43
  expect(e.evaluate(Expression('add::type()'), s)).toBe('function');
30
44
  });
31
45
  });
package/tests/main.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { Evaluator } from '../dist/main.js';
2
+
3
+ describe('Evaluator', () => {
4
+ it('should have logger', () => {
5
+ expect(new Evaluator().logger).toBeTruthy();
6
+ expect(new Evaluator(null).logger).toBeTruthy();
7
+ expect(new Evaluator({}).logger).toBeTruthy();
8
+ });
9
+
10
+ describe('can evaluate choices', () => {
11
+ const e = new Evaluator();
12
+
13
+ it('on logical type', () => {
14
+ const c0 = e.evaluateChoices({ type: 'logical', value: null as never }, null);
15
+ expect(c0.length).toBe(2);
16
+ expect(c0[0].key).toBe(false);
17
+ expect(c0[1].key).toBe(true);
18
+
19
+ const c1 = e.evaluateChoices(
20
+ {
21
+ type: 'logical',
22
+ // @ts-expect-error Incomplete choice definition
23
+ choices: [{}, { key: true, name: 'Yes' }],
24
+ value: null as never,
25
+ },
26
+ null,
27
+ );
28
+ expect(c1.length).toBe(2);
29
+ expect(c1[0].key).toBe(false);
30
+ expect(c1[1].key).toBe(true);
31
+ expect(c1[0].name).toBe('');
32
+ expect(c1[1].name).toBe('Yes');
33
+
34
+ const c2 = e.evaluateChoices(
35
+ {
36
+ type: 'logical',
37
+ // @ts-expect-error Incomplete choice definition
38
+ choices: [{ key: 0 }, { key: '1' }, { key: 2 }],
39
+ value: null as never,
40
+ },
41
+ null,
42
+ );
43
+ expect(c2.length).toBe(2);
44
+ expect(c2[0].key).toBe(0);
45
+ expect(c2[1].key).toBe(1);
46
+
47
+ const c3 = e.evaluateChoices({ type: 'logical', choices: [], value: 0 }, null);
48
+ expect(c3.length).toBe(2);
49
+ expect(c3[0].key).toBe(0);
50
+ expect(c3[1].key).toBe(1);
51
+
52
+ const c4 = e.evaluateChoices({ type: 'logical', value: 'x' }, null);
53
+ expect(c4.length).toBe(2);
54
+ expect(c4[0].key).toBe('0');
55
+ expect(c4[1].key).toBe('1');
56
+ });
57
+ });
58
+ });