@cloudpss/expression 0.6.0-alpha.8 → 0.6.0-beta.1

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 +3 -2
  48. package/dist/migrate.d.ts.map +1 -1
  49. package/dist/migrate.js +25 -3
  50. package/dist/migrate.js.map +1 -1
  51. package/dist/migrator/access.d.ts.map +1 -1
  52. package/dist/migrator/access.js +65 -30
  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 +2 -2
  81. package/dist/migrator/state.d.ts.map +1 -1
  82. package/dist/migrator/state.js +30 -33
  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 +49 -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 +17 -18
  103. package/dist/scope.d.ts.map +1 -1
  104. package/dist/scope.js +84 -53
  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 +30 -6
  125. package/src/migrator/access.ts +68 -38
  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 +30 -33
  135. package/src/migrator/symbol.ts +33 -12
  136. package/src/migrator/to-type.ts +23 -4
  137. package/src/migrator/utils.ts +49 -1
  138. package/src/parser.ts +33 -8
  139. package/src/re-exports.ts +47 -0
  140. package/src/scope.ts +101 -65
  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 +19 -11
  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
+ isVmExtern,
4
+ type VmAny,
5
+ type VmArray,
6
+ type VmContext,
7
+ type VmRecord,
8
+ type VmValue,
9
+ isVmModule,
10
+ } from '@mirascript/mirascript';
3
11
  import {
4
12
  type CompiledExpression,
5
13
  type Expression,
@@ -7,16 +15,27 @@ 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';
21
+ import type { Evaluator } from './main.js';
13
22
  const { hasOwn } = Object;
14
23
 
15
24
  const RAW = Symbol.for('@private/expression:raw');
16
25
 
17
26
  /** 是否需要创建代理对象 */
18
- function needsProxy(value: unknown): value is Expression | object {
19
- return isExpression(value) || (value != null && typeof value == 'object');
27
+ function needsProxy(value: ExpressionOrValue | undefined): value is Expression | VmArray | VmRecord {
28
+ // 先检查 isExpression,因为 'function' 也有可能是 Expression
29
+ if (isExpression(value)) {
30
+ return true;
31
+ }
32
+ if (value == null || typeof value != 'object') {
33
+ return false;
34
+ }
35
+ if (isVmExtern(value) || isVmModule(value)) {
36
+ return false;
37
+ }
38
+ return true;
20
39
  }
21
40
 
22
41
  /** 创建代理对象 */
@@ -53,75 +72,99 @@ class ScopeProxyHandler implements ProxyHandler<object> {
53
72
  if (p === RAW) {
54
73
  return target;
55
74
  }
56
- const value = Reflect.get(target, p) as unknown;
57
- if (!needsProxy(value)) {
75
+ const value = Reflect.get(target, p) as ExpressionOrValue;
76
+ if (isVmExtern(target) || !needsProxy(value)) {
58
77
  return value;
59
78
  }
60
- if (isVmExtern(target)) {
61
- return createProxy(value, this.scope, this.path);
62
- }
63
79
  return createProxy(value, this.scope, [...this.path, p]);
64
80
  }
65
81
  }
66
82
 
83
+ /** 最大求值计数 */
84
+ const MAX_RECURSION = 500;
67
85
  /** 函数的执行环境 */
68
86
  export class Scope {
69
- /** 最大求值计数 */
70
- static readonly MAX_RECURSION = 500;
71
87
  constructor(
72
88
  /** 生成环境的工厂函数 */
73
- readonly scope?:
89
+ readonly scope:
74
90
  | ((key: string) => ExpressionOrValue | undefined)
75
- | Record<string, ExpressionOrValue | undefined>,
91
+ | Record<string, ExpressionOrValue | undefined>
92
+ | null = null,
76
93
  /** 求值失败是否抛异常 */
77
94
  readonly throws = true,
78
95
  /** 执行环境名称 */
79
96
  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
- }
97
+ ) {}
98
+ /** 枚举环境中的值 */
99
+ enumerator: (() => Iterable<string>) | null | undefined = null;
100
+ /** 描述环境中的值 */
101
+ describer: ((key: string) => string | undefined) | null | undefined = null;
99
102
 
100
- protected readonly getter: (key: string) => VmAny;
101
- /** 上下文 */
102
- protected context: Context | null = null;
103
+ /** 执行器上下文 */
104
+ protected evaluator: Evaluator | null = null;
103
105
  /** 求值计数 */
104
106
  protected evalCounter = 0;
105
- /** 重置执行环境 */
106
- reset(context: Context | null): () => void {
107
- const oldContext = this.context;
107
+ /** 重置执行器 */
108
+ reset(evaluator: Evaluator | null): () => void {
109
+ const oldEvaluator = this.evaluator;
108
110
  const oldCounter = this.evalCounter;
109
- this.context = context;
111
+ this.evaluator = evaluator;
110
112
  this.evalCounter = 0;
111
113
  return () => {
112
- this.context = oldContext;
114
+ this.evaluator = oldEvaluator;
113
115
  this.evalCounter = oldCounter;
114
116
  };
115
117
  }
116
118
  /** 函数的执行环境,惰性求值 */
117
- protected _proxy?: VmContext;
119
+ protected _proxy: VmContext | null = null;
118
120
  /** 函数的执行环境,惰性求值 */
119
121
  get proxy(): VmContext {
120
- this._proxy ??= createVmContext(this.getter);
121
- return this._proxy;
122
+ if (this._proxy != null) return this._proxy;
123
+ let getter: (key: string) => VmAny;
124
+ let enumerate: () => Iterable<string>;
125
+ let describe: ((key: string) => string | undefined) | undefined;
126
+ const { scope, enumerator, describer } = this;
127
+ if (scope == null) {
128
+ getter = (key) => this.evaluator?.imported.get(key);
129
+ enumerate = () => this.evaluator?.imported.keys() ?? [];
130
+ describe = () => undefined;
131
+ } else if (typeof scope == 'function') {
132
+ getter = (key) => {
133
+ const v = scope(key);
134
+ if (v === undefined) return this.evaluator?.imported.get(key);
135
+ if (!needsProxy(v)) return v;
136
+ return createProxy(v, this, [key]) as VmValue;
137
+ };
138
+ } else {
139
+ getter = (key) => {
140
+ if (!hasOwn(scope, key)) return this.evaluator?.imported.get(key);
141
+ const v = scope[key];
142
+ if (!needsProxy(v)) return v;
143
+ return createProxy(v, this, [key]) as VmValue;
144
+ };
145
+ }
146
+ if (typeof enumerator == 'function') {
147
+ enumerate = () => {
148
+ const keys = enumerator();
149
+ const imported = this.evaluator?.imported.keys() ?? [];
150
+ return [...keys, ...imported];
151
+ };
152
+ } else {
153
+ enumerate = () => this.evaluator?.imported.keys() ?? [];
154
+ }
155
+ if (typeof describer == 'function') {
156
+ describe = (key) => {
157
+ const d = describer(key);
158
+ if (d) return d;
159
+ return undefined;
160
+ };
161
+ }
162
+ const proxy = createVmContext(getter, enumerate, describe);
163
+ this._proxy = proxy;
164
+ return proxy;
122
165
  }
123
166
 
124
- /** 检查环境中是否有值 */
167
+ /** 检查环境中是否有值,不包含 evaluator 和 default context 中的值 */
125
168
  has(key: string): boolean {
126
169
  const { scope } = this;
127
170
  if (scope == null) return false;
@@ -134,8 +177,8 @@ export class Scope {
134
177
  return v !== undefined;
135
178
  }
136
179
 
137
- /** 获取环境中的值 */
138
- get(key: string): VmAny {
180
+ /** 获取环境中的值,不包含 evaluator 和 default context 中的值 */
181
+ get<V extends VmValue>(key: string): V | undefined {
139
182
  const { scope } = this;
140
183
  if (scope == null) return undefined;
141
184
  let v;
@@ -144,41 +187,34 @@ export class Scope {
144
187
  } else if (hasOwn(scope, key)) {
145
188
  v = scope[key];
146
189
  }
147
- if (!isExpression(v)) return v;
148
- return this.eval(v, [key]);
190
+ if (!isExpression(v)) return v satisfies VmValue | undefined as V | undefined;
191
+ return this.eval(v, [key]) satisfies VmValue as V;
149
192
  }
150
193
 
151
- /**
152
- * 表达式递归求值
153
- */
194
+ /** 表达式递归求值 */
154
195
  eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue {
155
- if (this.evalCounter >= Scope.MAX_RECURSION) {
196
+ if (this.evalCounter >= MAX_RECURSION) {
197
+ this.evalCounter = 0;
156
198
  throw new Error(`Execution recursion exceeds limit`);
157
199
  }
158
200
 
159
201
  this.evalCounter++;
160
202
  try {
161
- let result;
203
+ let result: VmValue | null = null;
162
204
  if (typeof expression == 'function') {
163
- result = expression(this) as VmValue;
205
+ result = expression(this, this.evaluator!);
164
206
  } else {
165
- const { context } = this;
166
- if (!context) {
167
- throw new Error(`Undefined context`);
168
- }
169
- const exp = parse(context, expression.source, this.throws);
207
+ const { evaluator } = this;
208
+ const exp = parse(evaluator, expression.source, this.throws, false);
170
209
  if (exp.func != null) {
171
- result = evaluateEval(context, exp, this);
210
+ result = evaluateEval(evaluator, exp, this);
172
211
  } else if (exp.error != null) {
173
212
  throw exp.error;
174
- } else {
175
- result = DEFAULTS;
176
213
  }
177
214
  }
178
- if (result === DEFAULTS) return null;
179
215
  const tag = expression[ExpressionTag];
180
216
  if (!tag) return result;
181
- return TypeInfo.to(result, TypeInfo.parse(tag));
217
+ return TypeInfo.to(result, tag);
182
218
  } catch (ex) {
183
219
  throw new Error(`${(ex as Error).message || String(ex)}\nIn ${this.name}: @ ${path.join('/')}`);
184
220
  }
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
  });