@cloudpss/expression 0.6.0-alpha.1 → 0.6.0-alpha.10

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 (150) hide show
  1. package/dist/analyze.d.ts +6 -3
  2. package/dist/analyze.d.ts.map +1 -1
  3. package/dist/analyze.js +67 -33
  4. package/dist/analyze.js.map +1 -1
  5. package/dist/definitions/argument.d.ts +2 -10
  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-decoration.d.ts +3 -3
  12. package/dist/definitions/parameter-decoration.d.ts.map +1 -1
  13. package/dist/definitions/parameter-decoration.js +0 -7
  14. package/dist/definitions/parameter-decoration.js.map +1 -1
  15. package/dist/definitions/parameter-group.d.ts +0 -4
  16. package/dist/definitions/parameter-group.d.ts.map +1 -1
  17. package/dist/definitions/parameter-group.js +5 -17
  18. package/dist/definitions/parameter-group.js.map +1 -1
  19. package/dist/definitions/parameter.d.ts +47 -40
  20. package/dist/definitions/parameter.d.ts.map +1 -1
  21. package/dist/definitions/parameter.js +9 -21
  22. package/dist/definitions/parameter.js.map +1 -1
  23. package/dist/definitions/utils.d.ts +19 -0
  24. package/dist/definitions/utils.d.ts.map +1 -0
  25. package/dist/definitions/utils.js +177 -0
  26. package/dist/definitions/utils.js.map +1 -0
  27. package/dist/definitions/variable.d.ts +0 -2
  28. package/dist/definitions/variable.d.ts.map +1 -1
  29. package/dist/definitions/variable.js +1 -5
  30. package/dist/definitions/variable.js.map +1 -1
  31. package/dist/definitions.d.ts +1 -0
  32. package/dist/definitions.d.ts.map +1 -1
  33. package/dist/definitions.js +1 -0
  34. package/dist/definitions.js.map +1 -1
  35. package/dist/eval.d.ts +3 -5
  36. package/dist/eval.d.ts.map +1 -1
  37. package/dist/eval.js +12 -20
  38. package/dist/eval.js.map +1 -1
  39. package/dist/expression.d.ts +10 -4
  40. package/dist/expression.d.ts.map +1 -1
  41. package/dist/expression.js +4 -4
  42. package/dist/expression.js.map +1 -1
  43. package/dist/index.d.ts +3 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/interface.d.ts +30 -0
  48. package/dist/interface.d.ts.map +1 -0
  49. package/dist/interface.js +6 -0
  50. package/dist/interface.js.map +1 -0
  51. package/dist/main.d.ts +41 -28
  52. package/dist/main.d.ts.map +1 -1
  53. package/dist/main.js +145 -141
  54. package/dist/main.js.map +1 -1
  55. package/dist/migrate.d.ts +3 -2
  56. package/dist/migrate.d.ts.map +1 -1
  57. package/dist/migrate.js +12 -3
  58. package/dist/migrate.js.map +1 -1
  59. package/dist/migrator/access.d.ts.map +1 -1
  60. package/dist/migrator/access.js +22 -24
  61. package/dist/migrator/access.js.map +1 -1
  62. package/dist/migrator/call.d.ts.map +1 -1
  63. package/dist/migrator/call.js +235 -209
  64. package/dist/migrator/call.js.map +1 -1
  65. package/dist/migrator/interface.d.ts +3 -1
  66. package/dist/migrator/interface.d.ts.map +1 -1
  67. package/dist/migrator/interface.js.map +1 -1
  68. package/dist/migrator/node.js +1 -1
  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 +19 -5
  72. package/dist/migrator/operator.js.map +1 -1
  73. package/dist/migrator/state.d.ts +2 -2
  74. package/dist/migrator/state.d.ts.map +1 -1
  75. package/dist/migrator/state.js +1 -2
  76. package/dist/migrator/state.js.map +1 -1
  77. package/dist/migrator/symbol.d.ts.map +1 -1
  78. package/dist/migrator/symbol.js +13 -0
  79. package/dist/migrator/symbol.js.map +1 -1
  80. package/dist/migrator/to-type.d.ts.map +1 -1
  81. package/dist/migrator/to-type.js +16 -0
  82. package/dist/migrator/to-type.js.map +1 -1
  83. package/dist/migrator/utils.d.ts +2 -0
  84. package/dist/migrator/utils.d.ts.map +1 -1
  85. package/dist/migrator/utils.js +18 -0
  86. package/dist/migrator/utils.js.map +1 -1
  87. package/dist/parser.d.ts +2 -2
  88. package/dist/parser.d.ts.map +1 -1
  89. package/dist/parser.js +25 -8
  90. package/dist/parser.js.map +1 -1
  91. package/dist/re-exports.d.ts +4 -0
  92. package/dist/re-exports.d.ts.map +1 -0
  93. package/dist/re-exports.js +3 -0
  94. package/dist/re-exports.js.map +1 -0
  95. package/dist/scope.d.ts +13 -16
  96. package/dist/scope.d.ts.map +1 -1
  97. package/dist/scope.js +56 -49
  98. package/dist/scope.js.map +1 -1
  99. package/dist/type.d.ts +14 -5
  100. package/dist/type.d.ts.map +1 -1
  101. package/dist/type.js +35 -11
  102. package/dist/type.js.map +1 -1
  103. package/package.json +4 -4
  104. package/src/analyze.ts +77 -37
  105. package/src/definitions/argument.ts +2 -12
  106. package/src/definitions/constraint.ts +3 -3
  107. package/src/definitions/parameter-decoration.ts +3 -9
  108. package/src/definitions/parameter-group.ts +4 -19
  109. package/src/definitions/parameter.ts +62 -61
  110. package/src/definitions/utils.ts +175 -0
  111. package/src/definitions/variable.ts +1 -6
  112. package/src/definitions.ts +1 -0
  113. package/src/eval.ts +13 -26
  114. package/src/expression.ts +14 -6
  115. package/src/index.ts +3 -1
  116. package/src/interface.ts +35 -0
  117. package/src/main.ts +213 -194
  118. package/src/migrate.ts +15 -6
  119. package/src/migrator/access.ts +21 -26
  120. package/src/migrator/call.ts +225 -194
  121. package/src/migrator/interface.ts +3 -1
  122. package/src/migrator/node.ts +1 -1
  123. package/src/migrator/operator.ts +19 -5
  124. package/src/migrator/state.ts +2 -2
  125. package/src/migrator/symbol.ts +13 -0
  126. package/src/migrator/to-type.ts +16 -0
  127. package/src/migrator/utils.ts +21 -0
  128. package/src/parser.ts +27 -8
  129. package/src/re-exports.ts +28 -0
  130. package/src/scope.ts +75 -61
  131. package/src/type.ts +32 -11
  132. package/tests/analyze.ts +40 -6
  133. package/tests/compile.ts +65 -0
  134. package/tests/condition.ts +13 -5
  135. package/tests/definition.ts +205 -18
  136. package/tests/eval-complex.ts +7 -10
  137. package/tests/eval.ts +59 -12
  138. package/tests/import.ts +18 -4
  139. package/tests/main.ts +9 -0
  140. package/tests/migrate.ts +77 -0
  141. package/tests/scope.ts +3 -3
  142. package/tests/template.ts +36 -0
  143. package/dist/context.d.ts +0 -41
  144. package/dist/context.d.ts.map +0 -1
  145. package/dist/context.js +0 -18
  146. package/dist/context.js.map +0 -1
  147. package/jest.config.js +0 -3
  148. package/src/context.ts +0 -54
  149. package/tests/tsconfig.json +0 -3
  150. package/tsconfig.json +0 -3
package/src/parser.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { compileSync, type VmScript } from '@mirascript/mirascript';
2
- import type { Context } from './context.js';
2
+ import { LRUCache } from 'lru-cache';
3
+ import type { Evaluator } from './main.js';
4
+
3
5
  /** 一般表达式 */
4
6
  export interface EvalExpressionCache {
5
7
  /**
@@ -38,12 +40,12 @@ export type ExpressionCache = ErrorExpressionCache | EvalExpressionCache;
38
40
  /**
39
41
  * 编译表达式
40
42
  */
41
- function compile(expression: string): ExpressionCache {
43
+ function compile(expression: string, template: boolean): ExpressionCache {
42
44
  const compiled = {
43
45
  source: expression,
44
46
  } as ExpressionCache;
45
47
  try {
46
- const script = compileSync(expression);
48
+ const script = compileSync(expression, { input_mode: template ? 'Template' : 'Script' });
47
49
  compiled.func = script;
48
50
  } catch (ex) {
49
51
  compiled.error = ex as Error;
@@ -51,19 +53,36 @@ function compile(expression: string): ExpressionCache {
51
53
  return compiled;
52
54
  }
53
55
 
56
+ const kTemplateCache = Symbol('cloudpss.expression.templateCache');
57
+ const kExpressionCache = Symbol('cloudpss.expression.expressionCache');
58
+
59
+ /** 获取编译缓存 */
60
+ function getCache(evaluator: Evaluator, template: boolean): LRUCache<string, EvalExpressionCache> {
61
+ const key = template ? kTemplateCache : kExpressionCache;
62
+ if (!(key in evaluator)) {
63
+ const size = template ? evaluator.options.templateCacheSize : evaluator.options.expressionCacheSize;
64
+ Object.defineProperty(evaluator, key, {
65
+ value: new LRUCache<string, EvalExpressionCache>({ max: size! || 50 }),
66
+ });
67
+ }
68
+ return (evaluator as Evaluator & Record<typeof key, LRUCache<string, EvalExpressionCache>>)[key];
69
+ }
70
+
54
71
  /**
55
72
  * 解析表达式
56
73
  */
57
- export function parse(context: Context, expression: string, throws: boolean): ExpressionCache {
74
+ export function parse(evaluator: Evaluator, expression: string, throws: boolean, template: boolean): ExpressionCache {
58
75
  if (typeof expression != 'string') {
59
76
  throw new TypeError(`${String(expression)} is not a valid expression`);
60
77
  }
61
- const v = context.expressionCache.get(expression);
62
- if (v) return v;
63
- const complied = compile(expression);
78
+ const cache = getCache(evaluator, template);
79
+ const cached = cache.get(expression);
80
+ if (cached != null) return cached;
81
+ const complied = compile(expression, template);
64
82
  if (complied.error) {
65
83
  if (throws) throw complied.error;
84
+ } else {
85
+ cache.set(expression, complied);
66
86
  }
67
- context.expressionCache.set(expression, complied);
68
87
  return complied;
69
88
  }
@@ -0,0 +1,28 @@
1
+ export {
2
+ isVmAny,
3
+ isVmArray,
4
+ isVmConst,
5
+ isVmContext,
6
+ isVmExtern,
7
+ isVmFunction,
8
+ isVmImmutable,
9
+ isVmModule,
10
+ isVmPrimitive,
11
+ isVmRecord,
12
+ isVmValue,
13
+ VmExtern,
14
+ VmFunction,
15
+ VmModule,
16
+ } from '@mirascript/mirascript';
17
+ export { serialize, serializePropName, serializeString, lib, operations } from '@mirascript/mirascript/subtle';
18
+ export type {
19
+ VmAny,
20
+ VmArray,
21
+ VmConst,
22
+ VmContext,
23
+ VmImmutable,
24
+ VmPrimitive,
25
+ VmRecord,
26
+ VmUninitialized,
27
+ VmValue,
28
+ } from '@mirascript/mirascript';
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,13 +72,10 @@ 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
  }
@@ -70,58 +86,61 @@ export class Scope {
70
86
  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
+ ) {}
99
98
 
100
- protected readonly getter: (key: string) => VmAny;
101
- /** 上下文 */
102
- protected context: Context | null = null;
99
+ /** 执行器上下文 */
100
+ protected evaluator: Evaluator | null = null;
103
101
  /** 求值计数 */
104
102
  protected evalCounter = 0;
105
- /** 重置执行环境 */
106
- reset(context: Context | null): () => void {
107
- const oldContext = this.context;
103
+ /** 重置执行器 */
104
+ reset(evaluator: Evaluator | null): () => void {
105
+ const oldEvaluator = this.evaluator;
108
106
  const oldCounter = this.evalCounter;
109
- this.context = context;
107
+ this.evaluator = evaluator;
110
108
  this.evalCounter = 0;
111
109
  return () => {
112
- this.context = oldContext;
110
+ this.evaluator = oldEvaluator;
113
111
  this.evalCounter = oldCounter;
114
112
  };
115
113
  }
116
114
  /** 函数的执行环境,惰性求值 */
117
- protected _proxy?: VmContext;
115
+ protected _proxy: VmContext | null = null;
118
116
  /** 函数的执行环境,惰性求值 */
119
117
  get proxy(): VmContext {
120
- this._proxy ??= createVmContext(this.getter);
121
- return this._proxy;
118
+ if (this._proxy != null) return this._proxy;
119
+ let getter: (key: string) => VmAny;
120
+ const { scope } = this;
121
+ if (scope == null) {
122
+ getter = (key) => this.evaluator?.imported.get(key);
123
+ } else if (typeof scope == 'function') {
124
+ getter = (key) => {
125
+ const v = scope(key);
126
+ if (v === undefined) return this.evaluator?.imported.get(key);
127
+ if (!needsProxy(v)) return v;
128
+ return createProxy(v, this, [key]) as VmValue;
129
+ };
130
+ } else {
131
+ getter = (key) => {
132
+ if (!hasOwn(scope, key)) return this.evaluator?.imported.get(key);
133
+ const v = scope[key];
134
+ if (!needsProxy(v)) return v;
135
+ return createProxy(v, this, [key]) as VmValue;
136
+ };
137
+ }
138
+ const proxy = createVmContext(getter);
139
+ this._proxy = proxy;
140
+ return proxy;
122
141
  }
123
142
 
124
- /** 检查环境中是否有值 */
143
+ /** 检查环境中是否有值,不包含 evaluator 和 default context 中的值 */
125
144
  has(key: string): boolean {
126
145
  const { scope } = this;
127
146
  if (scope == null) return false;
@@ -134,8 +153,8 @@ export class Scope {
134
153
  return v !== undefined;
135
154
  }
136
155
 
137
- /** 获取环境中的值 */
138
- get(key: string): VmAny {
156
+ /** 获取环境中的值,不包含 evaluator 和 default context 中的值 */
157
+ get<V extends VmValue>(key: string): V | undefined {
139
158
  const { scope } = this;
140
159
  if (scope == null) return undefined;
141
160
  let v;
@@ -144,13 +163,11 @@ export class Scope {
144
163
  } else if (hasOwn(scope, key)) {
145
164
  v = scope[key];
146
165
  }
147
- if (!isExpression(v)) return v;
148
- return this.eval(v, [key]);
166
+ if (!isExpression(v)) return v satisfies VmValue | undefined as V | undefined;
167
+ return this.eval(v, [key]) satisfies VmValue as V;
149
168
  }
150
169
 
151
- /**
152
- * 表达式递归求值
153
- */
170
+ /** 表达式递归求值 */
154
171
  eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue {
155
172
  if (this.evalCounter >= Scope.MAX_RECURSION) {
156
173
  throw new Error(`Execution recursion exceeds limit`);
@@ -158,27 +175,24 @@ export class Scope {
158
175
 
159
176
  this.evalCounter++;
160
177
  try {
161
- let result;
178
+ let result: VmValue | null = null;
162
179
  if (typeof expression == 'function') {
163
- result = expression(this) as VmValue;
180
+ result = expression(this, this.evaluator!);
164
181
  } else {
165
- const { context } = this;
166
- if (!context) {
167
- throw new Error(`Undefined context`);
182
+ const { evaluator } = this;
183
+ if (!evaluator) {
184
+ throw new Error(`Undefined evaluator`);
168
185
  }
169
- const exp = parse(context, expression.source, this.throws);
186
+ const exp = parse(evaluator, expression.source, this.throws, false);
170
187
  if (exp.func != null) {
171
- result = evaluateEval(context, exp, this);
188
+ result = evaluateEval(evaluator, exp, this);
172
189
  } else if (exp.error != null) {
173
190
  throw exp.error;
174
- } else {
175
- result = DEFAULTS;
176
191
  }
177
192
  }
178
- if (result === DEFAULTS) return null;
179
193
  const tag = expression[ExpressionTag];
180
194
  if (!tag) return result;
181
- return TypeInfo.to(result, TypeInfo.parse(tag));
195
+ return TypeInfo.to(result, tag);
182
196
  } catch (ex) {
183
197
  throw new Error(`${(ex as Error).message || String(ex)}\nIn ${this.name}: @ ${path.join('/')}`);
184
198
  }
package/src/type.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { TypeName, VmValue, VmValueMap } from '@mirascript/mirascript';
2
2
  import { lib } from '@mirascript/mirascript/subtle';
3
+ const { to_string, to_number, to_boolean } = lib;
3
4
 
4
5
  /** 类型信息 */
5
6
  export type TypeInfo = 'string' | 'number' | 'boolean';
@@ -21,14 +22,16 @@ export type TsTypeOf<T extends TypeInfo | LegacyType> = TypeMap[T];
21
22
  export type LegacyType = 's' | 'f' | 'b';
22
23
 
23
24
  /** 类型信息 */
24
- export const TypeInfo = {
25
- parse: (type: TypeInfo | LegacyType): TypeInfo => {
25
+ export namespace TypeInfo {
26
+ /** 解析类型 */
27
+ export function parse(type: TypeInfo | LegacyType): TypeInfo {
26
28
  if (type === 's' || type === 'string') return 'string';
27
29
  if (type === 'f' || type === 'number') return 'number';
28
30
  if (type === 'b' || type === 'boolean') return 'boolean';
29
31
  throw new TypeError(`Invalid type '${String(type)}'`);
30
- },
31
- is: <T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): value is TsTypeOf<T> => {
32
+ }
33
+ /** 判断类型 */
34
+ export function is<T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): value is TsTypeOf<T> {
32
35
  switch (type) {
33
36
  case 'b':
34
37
  case 'boolean':
@@ -43,24 +46,42 @@ export const TypeInfo = {
43
46
  (type) satisfies never;
44
47
  return false;
45
48
  }
46
- },
47
- to: <T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): TsTypeOf<T> => {
49
+ }
50
+
51
+ /** 转换类型 */
52
+ export function to<T extends TypeInfo | LegacyType>(value: VmValue | undefined, type: T): TsTypeOf<T> {
48
53
  /** 返回类型 */
49
54
  type R = TsTypeOf<T>;
55
+ value ??= null;
50
56
  if (!type) return value as R;
51
57
  switch (type) {
52
58
  case 'b':
53
59
  case 'boolean':
54
- return lib.to_boolean(value) as R;
60
+ return toBoolean(value) as R;
55
61
  case 'f':
56
62
  case 'number':
57
- return lib.to_number(value) as R;
63
+ return toNumber(value) as R;
58
64
  case 's':
59
65
  case 'string':
60
- return lib.to_string(value) as R;
66
+ return toString(value) as R;
61
67
  default:
62
68
  (type) satisfies never;
63
69
  return value as R;
64
70
  }
65
- },
66
- };
71
+ }
72
+
73
+ /** 转换为字符串 */
74
+ export function toString(value: VmValue | undefined): string {
75
+ return to_string(value ?? null);
76
+ }
77
+
78
+ /** 转换为数字 */
79
+ export function toNumber(value: VmValue | undefined): number {
80
+ return to_number(value ?? null);
81
+ }
82
+
83
+ /** 转换为布尔值 */
84
+ export function toBoolean(value: VmValue | undefined): boolean {
85
+ return to_boolean(value ?? null);
86
+ }
87
+ }
package/tests/analyze.ts CHANGED
@@ -1,15 +1,49 @@
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([]);
9
35
  });
10
36
  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']]);
37
+ expect(e.analyze(Expression('a .b [1]+b'), s)).toEqual([['a', 'b'], ['b']]);
38
+ expect(e.analyze(Expression('a. b["x"]+b'), s)).toEqual([['a', 'b'], ['b']]);
39
+ expect(e.analyze(Expression('a.b[x] + b + c.12.3'), s)).toEqual([['a', 'b'], ['x'], ['b'], ['c', 12, 3]]);
40
+ });
41
+ it('with lib', () => {
42
+ const e0 = new Evaluator();
43
+ const exp = Expression('sin(x) + y::z.t()::cos() + w.123');
44
+ expect(e.analyze(exp, s)).toEqual([['sin'], ['x'], ['z', 't'], ['y']]);
45
+ expect(e0.analyze(exp, s)).toEqual([['sin'], ['x'], ['z', 't'], ['y'], ['w', 123]]);
46
+ expect(e.analyze(exp)).toEqual([['x'], ['z', 't'], ['y']]);
47
+ expect(e0.analyze(exp)).toEqual([['x'], ['z', 't'], ['y'], ['w', 123]]);
14
48
  });
15
49
  });
@@ -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
  });
@@ -42,8 +51,7 @@ describe('Evaluate conditions', () => {
42
51
  expect(e.evaluateCondition('12<=3' as ExpressionSource<boolean>, s)).toBe(false);
43
52
  });
44
53
  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);
54
+ expect(en.evaluateCondition('12>>3' as ExpressionSource<boolean>, undefined, true)).toBe(true);
55
+ expect(en.evaluateCondition('12>>3' as ExpressionSource<boolean>, null, false)).toBe(false);
48
56
  });
49
57
  });