@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
package/src/scope.ts CHANGED
@@ -1,5 +1,13 @@
1
- import { createVmContext, isVmExtern, type VmAny, type VmContext, type VmValue } from '@mirascript/mirascript';
2
- import type { Context } from './context.js';
1
+ import {
2
+ createVmContext,
3
+ type VmAny,
4
+ type VmArray,
5
+ type VmContext,
6
+ type VmRecord,
7
+ type VmValue,
8
+ isVmWrapper,
9
+ isVmFunction,
10
+ } from '@mirascript/mirascript';
3
11
  import {
4
12
  type CompiledExpression,
5
13
  type Expression,
@@ -7,41 +15,57 @@ import {
7
15
  ExpressionTag,
8
16
  isExpression,
9
17
  } from './expression.js';
10
- import { DEFAULTS, evaluateEval } from './eval.js';
18
+ import { evaluateEval } from './eval.js';
11
19
  import { parse } from './parser.js';
12
20
  import { TypeInfo } from './type.js';
13
- const { hasOwn } = Object;
21
+ import type { Evaluator } from './main.js';
22
+ const { hasOwn, keys } = Object;
23
+ const { isArray } = Array;
14
24
 
15
25
  const RAW = Symbol.for('@private/expression:raw');
16
26
 
17
27
  /** 是否需要创建代理对象 */
18
- function needsProxy(value: unknown): value is Expression | object {
19
- return isExpression(value) || (value != null && typeof value == 'object');
28
+ function isSpecialHostObject(value: object): boolean {
29
+ return (
30
+ value instanceof Date ||
31
+ value instanceof Promise ||
32
+ value instanceof RegExp ||
33
+ ArrayBuffer.isView(value) ||
34
+ value instanceof ArrayBuffer ||
35
+ (typeof SharedArrayBuffer != 'undefined' && value instanceof SharedArrayBuffer)
36
+ );
37
+ }
38
+
39
+ /** 是否需要创建代理对象 */
40
+ function needsProxy(value: ExpressionOrValue | undefined): value is Expression | VmArray | VmRecord {
41
+ if (value == null || (typeof value != 'object' && typeof value != 'function')) {
42
+ return false;
43
+ }
44
+ if (isExpression(value)) {
45
+ return true;
46
+ }
47
+ if (isVmWrapper(value) || isVmFunction(value) || isSpecialHostObject(value)) {
48
+ return false;
49
+ }
50
+ return true;
20
51
  }
21
52
 
22
53
  /** 创建代理对象 */
23
- function createProxy(value: Expression | object, scope: Scope, path: readonly PropertyKey[]): unknown {
24
- // 先检查 isExpression,因为 'function' 也有可能是 Expression
54
+ function createProxy(value: Expression | VmArray | VmRecord, scope: Scope, path: readonly PropertyKey[]): unknown {
25
55
  if (isExpression(value)) {
26
56
  return scope.eval(value, path);
27
57
  }
28
58
  if (typeof value != 'object' || value == null) {
29
59
  return value;
30
60
  }
31
- if (
32
- value instanceof Date ||
33
- value instanceof Promise ||
34
- value instanceof RegExp ||
35
- ArrayBuffer.isView(value) ||
36
- value instanceof ArrayBuffer ||
37
- (typeof SharedArrayBuffer != 'undefined' && value instanceof SharedArrayBuffer)
38
- ) {
39
- return value;
61
+ if (isArray(value)) {
62
+ return new Proxy(value, new ArrayScopeProxyHandler(scope, path));
63
+ } else {
64
+ return new Proxy(value, new RecordScopeProxyHandler(scope, path));
40
65
  }
41
- return new Proxy(value, new ScopeProxyHandler(scope, path));
42
66
  }
43
67
  /** 函数的执行环境 */
44
- class ScopeProxyHandler implements ProxyHandler<object> {
68
+ class ArrayScopeProxyHandler implements ProxyHandler<object> {
45
69
  constructor(
46
70
  readonly scope: Scope,
47
71
  readonly path: readonly PropertyKey[],
@@ -53,75 +77,119 @@ class ScopeProxyHandler implements ProxyHandler<object> {
53
77
  if (p === RAW) {
54
78
  return target;
55
79
  }
56
- const value = Reflect.get(target, p) as unknown;
57
- if (!needsProxy(value)) {
80
+ const value = Reflect.get(target, p) as ExpressionOrValue;
81
+ if (typeof p == 'symbol' || String(Number(p)) !== p || !needsProxy(value)) {
58
82
  return value;
59
83
  }
60
- if (isVmExtern(target)) {
61
- return createProxy(value, this.scope, this.path);
84
+ return createProxy(value, this.scope, [...this.path, p]);
85
+ }
86
+ }
87
+ /** 函数的执行环境 */
88
+ class RecordScopeProxyHandler implements ProxyHandler<object> {
89
+ constructor(
90
+ readonly scope: Scope,
91
+ readonly path: readonly PropertyKey[],
92
+ ) {}
93
+ /**
94
+ * @inheritdoc
95
+ */
96
+ get(target: object, p: string | symbol): unknown {
97
+ if (p === RAW) {
98
+ return target;
99
+ }
100
+ const value = Reflect.get(target, p) as ExpressionOrValue;
101
+ if (typeof p == 'symbol' || !hasOwn(target, p) || !needsProxy(value)) {
102
+ return value;
62
103
  }
63
104
  return createProxy(value, this.scope, [...this.path, p]);
64
105
  }
65
106
  }
66
107
 
108
+ /** 最大求值计数 */
109
+ const MAX_RECURSION = 500;
67
110
  /** 函数的执行环境 */
68
111
  export class Scope {
69
- /** 最大求值计数 */
70
- static readonly MAX_RECURSION = 500;
71
112
  constructor(
72
113
  /** 生成环境的工厂函数 */
73
- readonly scope?:
114
+ readonly scope:
74
115
  | ((key: string) => ExpressionOrValue | undefined)
75
- | Record<string, ExpressionOrValue | undefined>,
116
+ | Record<string, ExpressionOrValue | undefined>
117
+ | null = null,
76
118
  /** 求值失败是否抛异常 */
77
119
  readonly throws = true,
78
120
  /** 执行环境名称 */
79
121
  readonly name = '<anonymous>',
80
- ) {
81
- if (scope == null) {
82
- this.getter = (key) => this.context?.imported.get(key);
83
- } else if (typeof scope == 'function') {
84
- this.getter = (key) => {
85
- const v = scope(key);
86
- if (v === undefined) return this.context?.imported.get(key);
87
- if (!needsProxy(v)) return v;
88
- return createProxy(v, this, [key]) as VmValue;
89
- };
90
- } else {
91
- this.getter = (key) => {
92
- if (!hasOwn(scope, key)) return this.context?.imported.get(key);
93
- const v = scope[key];
94
- if (!needsProxy(v)) return v;
95
- return createProxy(v, this, [key]) as VmValue;
96
- };
97
- }
98
- }
122
+ ) {}
123
+ /** 枚举环境中的值 */
124
+ enumerator: (() => Iterable<string>) | null | undefined = null;
125
+ /** 描述环境中的值 */
126
+ describer: ((key: string) => string | undefined) | null | undefined = null;
99
127
 
100
- protected readonly getter: (key: string) => VmAny;
101
- /** 上下文 */
102
- protected context: Context | null = null;
128
+ /** 执行器上下文 */
129
+ protected evaluator: Evaluator | null = null;
103
130
  /** 求值计数 */
104
131
  protected evalCounter = 0;
105
- /** 重置执行环境 */
106
- reset(context: Context | null): () => void {
107
- const oldContext = this.context;
132
+ /** 重置执行器 */
133
+ reset(evaluator: Evaluator | null): () => void {
134
+ const oldEvaluator = this.evaluator;
108
135
  const oldCounter = this.evalCounter;
109
- this.context = context;
136
+ this.evaluator = evaluator;
110
137
  this.evalCounter = 0;
111
138
  return () => {
112
- this.context = oldContext;
139
+ this.evaluator = oldEvaluator;
113
140
  this.evalCounter = oldCounter;
114
141
  };
115
142
  }
116
143
  /** 函数的执行环境,惰性求值 */
117
- protected _proxy?: VmContext;
144
+ protected _proxy: VmContext | null = null;
118
145
  /** 函数的执行环境,惰性求值 */
119
146
  get proxy(): VmContext {
120
- this._proxy ??= createVmContext(this.getter);
121
- return this._proxy;
147
+ if (this._proxy != null) return this._proxy;
148
+ let getter: (key: string) => VmAny;
149
+ let enumerate: () => Iterable<string>;
150
+ let describe: ((key: string) => string | undefined) | undefined;
151
+ const { scope, enumerator, describer } = this;
152
+ if (scope == null) {
153
+ getter = (key) => this.evaluator?.imported.get(key);
154
+ enumerate = () => this.evaluator?.imported.keys() ?? [];
155
+ describe = () => undefined;
156
+ } else if (typeof scope == 'function') {
157
+ getter = (key) => {
158
+ const v = scope(key);
159
+ if (v === undefined) return this.evaluator?.imported.get(key);
160
+ if (!needsProxy(v)) return v;
161
+ return createProxy(v, this, [key]) as VmValue;
162
+ };
163
+ } else {
164
+ getter = (key) => {
165
+ if (!hasOwn(scope, key)) return this.evaluator?.imported.get(key);
166
+ const v = scope[key];
167
+ if (!needsProxy(v)) return v;
168
+ return createProxy(v, this, [key]) as VmValue;
169
+ };
170
+ }
171
+ if (typeof enumerator == 'function') {
172
+ enumerate = () => {
173
+ const keys = enumerator();
174
+ const imported = this.evaluator?.imported.keys() ?? [];
175
+ return [...keys, ...imported];
176
+ };
177
+ } else {
178
+ enumerate = () => this.evaluator?.imported.keys() ?? [];
179
+ }
180
+ if (typeof describer == 'function') {
181
+ describe = (key) => {
182
+ const d = describer(key);
183
+ if (d) return d;
184
+ return undefined;
185
+ };
186
+ }
187
+ const proxy = createVmContext(getter, enumerate, describe);
188
+ this._proxy = proxy;
189
+ return proxy;
122
190
  }
123
191
 
124
- /** 检查环境中是否有值 */
192
+ /** 检查环境中是否有值,不包含 evaluator 和 default context 中的值 */
125
193
  has(key: string): boolean {
126
194
  const { scope } = this;
127
195
  if (scope == null) return false;
@@ -134,7 +202,7 @@ export class Scope {
134
202
  return v !== undefined;
135
203
  }
136
204
 
137
- /** 获取环境中的值 */
205
+ /** 获取环境中的值,不包含 evaluator 和 default context 中的值 */
138
206
  get<V extends VmValue>(key: string): V | undefined {
139
207
  const { scope } = this;
140
208
  if (scope == null) return undefined;
@@ -148,37 +216,30 @@ export class Scope {
148
216
  return this.eval(v, [key]) satisfies VmValue as V;
149
217
  }
150
218
 
151
- /**
152
- * 表达式递归求值
153
- */
219
+ /** 表达式递归求值 */
154
220
  eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue {
155
- if (this.evalCounter >= Scope.MAX_RECURSION) {
221
+ if (this.evalCounter >= MAX_RECURSION) {
222
+ this.evalCounter = 0;
156
223
  throw new Error(`Execution recursion exceeds limit`);
157
224
  }
158
225
 
159
226
  this.evalCounter++;
160
227
  try {
161
- let result;
228
+ let result: VmValue | null = null;
162
229
  if (typeof expression == 'function') {
163
- result = expression(this) as VmValue;
230
+ result = expression(this, this.evaluator!);
164
231
  } else {
165
- const { context } = this;
166
- if (!context) {
167
- throw new Error(`Undefined context`);
168
- }
169
- const exp = parse(context, expression.source, this.throws);
232
+ const { evaluator } = this;
233
+ const exp = parse(evaluator, expression.source, this.throws, false);
170
234
  if (exp.func != null) {
171
- result = evaluateEval(context, exp, this);
235
+ result = evaluateEval(evaluator, exp, this);
172
236
  } else if (exp.error != null) {
173
237
  throw exp.error;
174
- } else {
175
- result = DEFAULTS;
176
238
  }
177
239
  }
178
- if (result === DEFAULTS) return null;
179
240
  const tag = expression[ExpressionTag];
180
241
  if (!tag) return result;
181
- return TypeInfo.to(result, TypeInfo.parse(tag));
242
+ return TypeInfo.to(result, tag);
182
243
  } catch (ex) {
183
244
  throw new Error(`${(ex as Error).message || String(ex)}\nIn ${this.name}: @ ${path.join('/')}`);
184
245
  }
@@ -190,15 +251,23 @@ function unwrapCore<T>(value: T): T | undefined {
190
251
  if (value == null || (typeof value != 'object' && typeof value != 'function')) {
191
252
  return undefined;
192
253
  }
254
+ if (isVmWrapper(value) || isVmFunction(value) || isSpecialHostObject(value)) {
255
+ return undefined;
256
+ }
257
+ // 是否有 proxy
193
258
  const raw = (value as T & { [RAW]?: T })[RAW];
259
+
260
+ // 不论是否被代理,均须递归处理子项
194
261
  const obj = raw ?? value;
195
- for (const key in obj) {
196
- if (!hasOwn(obj, key)) continue;
262
+ // 只处理 own enumerable 属性
263
+ for (const key of keys(obj) as Array<Extract<keyof T, string>>) {
197
264
  const unwrapped = unwrapCore(obj[key]);
198
265
  if (unwrapped !== undefined) {
199
266
  obj[key] = unwrapped;
200
267
  }
201
268
  }
269
+
270
+ // 返回被代理的对象,如没有 proxy 则返回 undefined
202
271
  return raw;
203
272
  }
204
273
 
package/src/type.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { TypeName, VmValue, VmValueMap } from '@mirascript/mirascript';
2
- import { lib } from '@mirascript/mirascript/subtle';
2
+ import { convert } from '@mirascript/mirascript/subtle';
3
3
 
4
4
  /** 类型信息 */
5
5
  export type TypeInfo = 'string' | 'number' | 'boolean';
@@ -12,10 +12,11 @@ type TypeMap = VmValueMap & {
12
12
  s: string;
13
13
  f: number;
14
14
  b: boolean;
15
+ '': VmValue;
15
16
  };
16
17
 
17
18
  /** Ts 类型 */
18
- export type TsTypeOf<T extends TypeInfo | LegacyType> = TypeMap[T];
19
+ export type TsTypeOf<T extends TypeInfo | LegacyType | '', I extends VmValue = VmValue> = T extends '' ? I : TypeMap[T];
19
20
 
20
21
  /** 兼容的旧类型 */
21
22
  export type LegacyType = 's' | 'f' | 'b';
@@ -48,38 +49,52 @@ export namespace TypeInfo {
48
49
  }
49
50
 
50
51
  /** 转换类型 */
51
- export function to<T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): TsTypeOf<T> {
52
- /** 返回类型 */
53
- type R = TsTypeOf<T>;
54
- if (!type) return value as R;
52
+ export function to<T extends TypeInfo | LegacyType | '', I extends VmValue = VmValue, F = undefined>(
53
+ value: I | undefined,
54
+ type: T,
55
+ fallback?: F,
56
+ ): TsTypeOf<T> | Exclude<F, undefined> {
57
+ const c = converter(type);
58
+ return (c as typeof convert.toNumber)(value, fallback) as TsTypeOf<T>;
59
+ }
60
+
61
+ /** 不转换 */
62
+ function identity<T extends VmValue, F = undefined>(
63
+ value: T | undefined,
64
+ _fallback?: F,
65
+ ): T /* | Exclude<F, undefined> */ {
66
+ if (value == null) return null as T;
67
+ return value;
68
+ }
69
+
70
+ /** 获取转换器 */
71
+ type Converters = {
72
+ string: typeof convert.toString;
73
+ s: typeof convert.toString;
74
+ number: typeof convert.toNumber;
75
+ f: typeof convert.toNumber;
76
+ boolean: typeof convert.toBoolean;
77
+ b: typeof convert.toBoolean;
78
+ '': typeof identity;
79
+ };
80
+ /** 获取转换器 */
81
+ export function converter<T extends TypeInfo | LegacyType | ''>(type: T): Converters[T] {
82
+ // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
55
83
  switch (type) {
56
84
  case 'b':
57
85
  case 'boolean':
58
- return lib.to_boolean(value) as R;
86
+ return toBoolean as Converters[T];
59
87
  case 'f':
60
88
  case 'number':
61
- return lib.to_number(value) as R;
89
+ return toNumber as Converters[T];
62
90
  case 's':
63
91
  case 'string':
64
- return lib.to_string(value) as R;
92
+ return toString as Converters[T];
65
93
  default:
66
- (type) satisfies never;
67
- return value as R;
94
+ (type) satisfies '';
95
+ return identity as Converters[T];
68
96
  }
69
97
  }
70
98
 
71
- /** 转换为字符串 */
72
- export function toString(value: VmValue | undefined): string {
73
- return lib.to_string(value);
74
- }
75
-
76
- /** 转换为数字 */
77
- export function toNumber(value: VmValue | undefined): number {
78
- return lib.to_number(value);
79
- }
80
-
81
- /** 转换为布尔值 */
82
- export function toBoolean(value: VmValue | undefined): boolean {
83
- return lib.to_boolean(value);
84
- }
99
+ export const { toBoolean, toFormat, toNumber, toString } = convert;
85
100
  }
package/tests/analyze.ts CHANGED
@@ -1,15 +1,54 @@
1
1
  import { Evaluator, Expression, Scope } from '../dist/index.js';
2
2
 
3
3
  const e = new Evaluator();
4
- const s = new Scope({}, false);
4
+ e.import({ w: 123 });
5
+ const s = new Scope({ sin: 12 }, false);
5
6
 
6
- describe.skip('Analyze', () => {
7
+ describe('Analyze', () => {
7
8
  it('simple', () => {
8
- expect(e.analyze(Expression('a+b'), s)).toEqual([['a'], ['b']]);
9
+ expect(e.analyze(Expression(' '), s)).toEqual([]);
10
+ expect(e.analyze(Expression('a + b + @c'), s)).toEqual([['a'], ['b'], ['@c']]);
11
+ });
12
+ it('bad', () => {
13
+ expect(e.analyze(Expression('a + '), s)).toEqual([]);
14
+ expect(e.analyze(Expression('a.b.'), s)).toEqual([]);
15
+ expect(() => e.analyze(Expression('a.b.'))).toThrow();
16
+ });
17
+ it('local', () => {
18
+ expect(e.analyze(Expression('let a = 12; a + nil'), s)).toEqual([]);
19
+ expect(e.analyze(Expression('let a = 12; a + nil'), s)).toEqual([]);
20
+ expect(e.analyze(Expression('let a = 12; a + b'), s)).toEqual([['b']]);
21
+ });
22
+ it('repeat', () => {
23
+ expect(e.analyze(Expression('a + b + a + a.b + b.c + a * a !. b'), s)).toEqual([
24
+ ['a'],
25
+ ['b'],
26
+ ['a', 'b'],
27
+ ['b', 'c'],
28
+ ]);
29
+ });
30
+ it('prefixed', () => {
31
+ expect(e.analyze(Expression('@@a + $b + c. $$ + $'), s)).toEqual([['@@a'], ['$b'], ['c', '$$'], ['$']]);
32
+ });
33
+ it('unicode', () => {
34
+ expect(e.analyze(Expression('变量.属性 + 变量2[索引] + 函数()'), s)).toEqual([
35
+ ['变量', '属性'],
36
+ ['变量2'],
37
+ ['索引'],
38
+ ['函数'],
39
+ ]);
9
40
  });
10
41
  it('access', () => {
11
- expect(e.analyze(Expression('a.b[1]+b'), s)).toEqual([['a', 'b'], ['b']]);
12
- expect(e.analyze(Expression('a.b["x"]+b'), s)).toEqual([['a', 'b', 'x'], ['b']]);
13
- expect(e.analyze(Expression('a.b[x]+b'), s)).toEqual([['a', 'b'], ['x'], ['b']]);
42
+ expect(e.analyze(Expression('a .b [1]+b'), s)).toEqual([['a', 'b'], ['b']]);
43
+ expect(e.analyze(Expression('a. b["x"]+b'), s)).toEqual([['a', 'b'], ['b']]);
44
+ expect(e.analyze(Expression('a.b[x] + b + c.12.3'), s)).toEqual([['a', 'b'], ['x'], ['b'], ['c', 12, 3]]);
45
+ });
46
+ it('with lib', () => {
47
+ const e0 = new Evaluator();
48
+ const exp = Expression('sin(x) + y::z.t()::cos() + w.123');
49
+ expect(e.analyze(exp, s)).toEqual([['sin'], ['x'], ['z', 't'], ['y']]);
50
+ expect(e0.analyze(exp, s)).toEqual([['sin'], ['x'], ['z', 't'], ['y'], ['w', 123]]);
51
+ expect(e.analyze(exp)).toEqual([['x'], ['z', 't'], ['y']]);
52
+ expect(e0.analyze(exp)).toEqual([['x'], ['z', 't'], ['y'], ['w', 123]]);
14
53
  });
15
54
  });
@@ -0,0 +1,65 @@
1
+ import { Evaluator, Expression, Scope } from '../dist/index.js';
2
+
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
+ });
11
+ const s = new Scope({}, false);
12
+
13
+ describe('Evaluator.compile should work correctly', () => {
14
+ it('should eval const', () => {
15
+ const result = e.compile(Expression('12'), false);
16
+ expect(result(s, e)).toBe(12);
17
+ expect(e.evaluate(result, s)).toBe(12);
18
+ });
19
+
20
+ it('should eval exp', () => {
21
+ const result = e.compile(Expression('exp(2)'), false);
22
+ expect(result(s, e)).toBe(Math.exp(2));
23
+ expect(e.evaluate(result, s)).toBe(Math.exp(2));
24
+ });
25
+
26
+ it('should eval error', () => {
27
+ const result = e.compile(Expression('exp+++'), false);
28
+ expect(result(s, e)).toBe(null);
29
+ expect(e.evaluate(result, s)).toBe(null);
30
+ });
31
+ });
32
+
33
+ describe('Evaluator.compile should convert result', () => {
34
+ it('should eval const', () => {
35
+ const result = e.compile(Expression('12', 'string'), false);
36
+ expect(result(s, e)).toBe('12');
37
+ expect(e.evaluate(result, s)).toBe('12');
38
+ });
39
+
40
+ it('should eval exp', () => {
41
+ const result = e.compile(Expression('exp(2)', 's'), false);
42
+ expect(result(s, e)).toBe(Math.exp(2).toString());
43
+ expect(e.evaluate(result, s)).toBe(Math.exp(2).toString());
44
+ });
45
+
46
+ it('should not convert defaults', () => {
47
+ {
48
+ const result = e.compile(Expression('nil', 's'), false);
49
+ expect(result(s, e)).toBe(null);
50
+ expect(e.evaluate(result, s, 1)).toBe(1);
51
+ }
52
+ {
53
+ // syntax error
54
+ const result = e.compile(Expression('exp+++', 's'), false);
55
+ expect(result(s, e)).toBe(null);
56
+ expect(e.evaluate(result, s, 1)).toBe(1);
57
+ }
58
+ {
59
+ // runtime error
60
+ const result = e.compile(Expression('x() + 1', 's'), false);
61
+ expect(result(s, e)).toBe(null);
62
+ expect(e.evaluate(result, s, 1)).toBe(1);
63
+ }
64
+ });
65
+ });
@@ -1,6 +1,16 @@
1
1
  import { Evaluator, type ExpressionSource, Scope } from '../dist/index.js';
2
2
 
3
- const e = new Evaluator();
3
+ const e = new Evaluator({
4
+ logger: undefined,
5
+ });
6
+ const en = new Evaluator({
7
+ logger: {
8
+ warn: () => void 0,
9
+ error: () => void 0,
10
+ info: () => void 0,
11
+ debug: () => void 0,
12
+ },
13
+ });
4
14
  const s = new Scope(undefined, false);
5
15
 
6
16
  describe('Evaluate conditions', () => {
@@ -9,7 +19,6 @@ describe('Evaluate conditions', () => {
9
19
  expect(e.evaluateCondition(false, s)).toBe(false);
10
20
  });
11
21
  it('can evaluate empty literals', () => {
12
- // @ts-expect-error null
13
22
  expect(e.evaluateCondition(null, s)).toBe(true);
14
23
  expect(e.evaluateCondition(undefined, s)).toBe(true);
15
24
  });
@@ -18,20 +27,28 @@ describe('Evaluate conditions', () => {
18
27
  expect(e.evaluateCondition('true' as ExpressionSource<boolean>, s)).toBe(true);
19
28
  expect(e.evaluateCondition('false' as ExpressionSource<boolean>, s)).toBe(false);
20
29
  });
21
- it('can convert to boolean', () => {
22
- expect(e.evaluateCondition('""' as ExpressionSource<boolean>, s)).toBe(true);
23
- expect(e.evaluateCondition('nil' as ExpressionSource<boolean>, s)).toBe(true);
24
- expect(e.evaluateCondition('undefined' as ExpressionSource<boolean>, s)).toBe(true);
25
- expect(e.evaluateCondition('0' as ExpressionSource<boolean>, s)).toBe(true);
26
- expect(e.evaluateCondition('nan' as ExpressionSource<boolean>, s)).toBe(true);
30
+ it('can evaluate to boolean', () => {
27
31
  expect(e.evaluateCondition('(false)' as ExpressionSource<boolean>, s)).toBe(false);
28
-
29
- expect(e.evaluateCondition('""' as ExpressionSource<boolean>, s, false)).toBe(true);
30
- expect(e.evaluateCondition('nil' as ExpressionSource<boolean>, s, false)).toBe(false);
31
- expect(e.evaluateCondition('undefined' as ExpressionSource<boolean>, s, false)).toBe(false);
32
- expect(e.evaluateCondition('0' as ExpressionSource<boolean>, s, false)).toBe(true);
33
- expect(e.evaluateCondition('nan' as ExpressionSource<boolean>, s, false)).toBe(true);
32
+ expect(e.evaluateCondition('(true)' as ExpressionSource<boolean>, s)).toBe(true);
34
33
  expect(e.evaluateCondition('(false)' as ExpressionSource<boolean>, s, false)).toBe(false);
34
+ expect(e.evaluateCondition('(true)' as ExpressionSource<boolean>, s, false)).toBe(true);
35
+ });
36
+ it('can evaluate to nil', () => {
37
+ expect(e.evaluateCondition('{}' as ExpressionSource<boolean>, s)).toBe(true);
38
+ expect(e.evaluateCondition('{}' as ExpressionSource<boolean>, s, false)).toBe(false);
39
+ });
40
+ it('do not convert to boolean', () => {
41
+ expect(en.evaluateCondition('""' as ExpressionSource<boolean>, s)).toBe(true);
42
+ expect(en.evaluateCondition('nil' as ExpressionSource<boolean>, s)).toBe(true);
43
+ expect(en.evaluateCondition('undefined' as ExpressionSource<boolean>, s)).toBe(true);
44
+ expect(en.evaluateCondition('0' as ExpressionSource<boolean>, s)).toBe(true);
45
+ expect(en.evaluateCondition('nan' as ExpressionSource<boolean>, s)).toBe(true);
46
+
47
+ expect(en.evaluateCondition('""' as ExpressionSource<boolean>, s, false)).toBe(false);
48
+ expect(en.evaluateCondition('nil' as ExpressionSource<boolean>, s, false)).toBe(false);
49
+ expect(en.evaluateCondition('undefined' as ExpressionSource<boolean>, s, false)).toBe(false);
50
+ expect(en.evaluateCondition('0' as ExpressionSource<boolean>, s, false)).toBe(false);
51
+ expect(en.evaluateCondition('nan' as ExpressionSource<boolean>, s, false)).toBe(false);
35
52
  });
36
53
  it('can evaluate complex', () => {
37
54
  expect(e.evaluateCondition('12>3' as ExpressionSource<boolean>, s)).toBe(true);
@@ -42,8 +59,7 @@ describe('Evaluate conditions', () => {
42
59
  expect(e.evaluateCondition('12<=3' as ExpressionSource<boolean>, s)).toBe(false);
43
60
  });
44
61
  it('can return default on error', () => {
45
- expect(e.evaluateCondition('12>>3' as ExpressionSource<boolean>, undefined, true)).toBe(true);
46
- // @ts-expect-error null
47
- expect(e.evaluateCondition('12>>3' as ExpressionSource<boolean>, null, false)).toBe(false);
62
+ expect(en.evaluateCondition('12>>3' as ExpressionSource<boolean>, undefined, true)).toBe(true);
63
+ expect(en.evaluateCondition('12>>3' as ExpressionSource<boolean>, null, false)).toBe(false);
48
64
  });
49
65
  });