@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.
- package/dist/analyze.d.ts +6 -3
- package/dist/analyze.d.ts.map +1 -1
- package/dist/analyze.js +13 -33
- package/dist/analyze.js.map +1 -1
- package/dist/definitions/argument.d.ts +3 -11
- package/dist/definitions/argument.d.ts.map +1 -1
- package/dist/definitions/constraint.d.ts +2 -2
- package/dist/definitions/constraint.d.ts.map +1 -1
- package/dist/definitions/constraint.js +1 -1
- package/dist/definitions/constraint.js.map +1 -1
- package/dist/definitions/parameter-group.js +4 -4
- package/dist/definitions/parameter-group.js.map +1 -1
- package/dist/definitions/parameter.d.ts +22 -14
- package/dist/definitions/parameter.d.ts.map +1 -1
- package/dist/definitions/parameter.js +10 -1
- package/dist/definitions/parameter.js.map +1 -1
- package/dist/definitions/utils.d.ts +28 -0
- package/dist/definitions/utils.d.ts.map +1 -0
- package/dist/definitions/utils.js +272 -0
- package/dist/definitions/utils.js.map +1 -0
- package/dist/definitions/variable.js +1 -1
- package/dist/definitions/variable.js.map +1 -1
- package/dist/definitions.d.ts +1 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +1 -1
- package/dist/definitions.js.map +1 -1
- package/dist/eval.d.ts +3 -5
- package/dist/eval.d.ts.map +1 -1
- package/dist/eval.js +12 -20
- package/dist/eval.js.map +1 -1
- package/dist/expression.d.ts +10 -4
- package/dist/expression.d.ts.map +1 -1
- package/dist/expression.js +6 -6
- package/dist/expression.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interface.d.ts +30 -0
- package/dist/interface.d.ts.map +1 -0
- package/dist/interface.js +6 -0
- package/dist/interface.js.map +1 -0
- package/dist/main.d.ts +41 -28
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +165 -149
- package/dist/main.js.map +1 -1
- package/dist/migrate.d.ts +3 -2
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +25 -3
- package/dist/migrate.js.map +1 -1
- package/dist/migrator/access.d.ts.map +1 -1
- package/dist/migrator/access.js +65 -30
- package/dist/migrator/access.js.map +1 -1
- package/dist/migrator/call.d.ts.map +1 -1
- package/dist/migrator/call.js +298 -201
- package/dist/migrator/call.js.map +1 -1
- package/dist/migrator/concat.d.ts.map +1 -1
- package/dist/migrator/concat.js +15 -2
- package/dist/migrator/concat.js.map +1 -1
- package/dist/migrator/function.d.ts +6 -0
- package/dist/migrator/function.d.ts.map +1 -0
- package/dist/migrator/function.js +25 -0
- package/dist/migrator/function.js.map +1 -0
- package/dist/migrator/interface.d.ts +3 -1
- package/dist/migrator/interface.d.ts.map +1 -1
- package/dist/migrator/interface.js.map +1 -1
- package/dist/migrator/node.d.ts.map +1 -1
- package/dist/migrator/node.js +55 -7
- package/dist/migrator/node.js.map +1 -1
- package/dist/migrator/operator.d.ts.map +1 -1
- package/dist/migrator/operator.js +107 -60
- package/dist/migrator/operator.js.map +1 -1
- package/dist/migrator/serialize.d.ts +4 -0
- package/dist/migrator/serialize.d.ts.map +1 -0
- package/dist/migrator/serialize.js +21 -0
- package/dist/migrator/serialize.js.map +1 -0
- package/dist/migrator/special.d.ts.map +1 -1
- package/dist/migrator/special.js +31 -0
- package/dist/migrator/special.js.map +1 -1
- package/dist/migrator/state.d.ts +2 -2
- package/dist/migrator/state.d.ts.map +1 -1
- package/dist/migrator/state.js +30 -33
- package/dist/migrator/state.js.map +1 -1
- package/dist/migrator/symbol.d.ts.map +1 -1
- package/dist/migrator/symbol.js +34 -12
- package/dist/migrator/symbol.js.map +1 -1
- package/dist/migrator/to-type.d.ts.map +1 -1
- package/dist/migrator/to-type.js +21 -4
- package/dist/migrator/to-type.js.map +1 -1
- package/dist/migrator/utils.d.ts +6 -0
- package/dist/migrator/utils.d.ts.map +1 -1
- package/dist/migrator/utils.js +49 -1
- package/dist/migrator/utils.js.map +1 -1
- package/dist/parser.d.ts +2 -2
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +27 -8
- package/dist/parser.js.map +1 -1
- package/dist/re-exports.d.ts +4 -0
- package/dist/re-exports.d.ts.map +1 -0
- package/dist/re-exports.js +3 -0
- package/dist/re-exports.js.map +1 -0
- package/dist/scope.d.ts +17 -18
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +84 -53
- package/dist/scope.js.map +1 -1
- package/dist/type.d.ts +22 -10
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +21 -24
- package/dist/type.js.map +1 -1
- package/package.json +8 -5
- package/src/analyze.ts +20 -39
- package/src/definitions/argument.ts +3 -13
- package/src/definitions/constraint.ts +3 -3
- package/src/definitions/parameter-group.ts +4 -4
- package/src/definitions/parameter.ts +47 -24
- package/src/definitions/utils.ts +288 -0
- package/src/definitions/variable.ts +1 -1
- package/src/definitions.ts +1 -28
- package/src/eval.ts +16 -25
- package/src/expression.ts +16 -8
- package/src/index.ts +3 -1
- package/src/interface.ts +35 -0
- package/src/main.ts +232 -200
- package/src/migrate.ts +30 -6
- package/src/migrator/access.ts +68 -38
- package/src/migrator/call.ts +287 -190
- package/src/migrator/concat.ts +15 -2
- package/src/migrator/function.ts +27 -0
- package/src/migrator/interface.ts +3 -1
- package/src/migrator/node.ts +56 -6
- package/src/migrator/operator.ts +110 -64
- package/src/migrator/serialize.ts +21 -0
- package/src/migrator/special.ts +31 -0
- package/src/migrator/state.ts +30 -33
- package/src/migrator/symbol.ts +33 -12
- package/src/migrator/to-type.ts +23 -4
- package/src/migrator/utils.ts +49 -1
- package/src/parser.ts +33 -8
- package/src/re-exports.ts +47 -0
- package/src/scope.ts +101 -65
- package/src/type.ts +40 -25
- package/tests/analyze.ts +45 -6
- package/tests/compile.ts +65 -0
- package/tests/condition.ts +33 -17
- package/tests/definition.ts +237 -18
- package/tests/eval-complex.ts +19 -11
- package/tests/eval.ts +59 -12
- package/tests/import.ts +21 -7
- package/tests/main.ts +58 -0
- package/tests/migrate.ts +317 -0
- package/tests/scope.ts +3 -3
- package/tests/template.ts +36 -0
- package/dist/context.d.ts +0 -41
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -18
- package/dist/context.js.map +0 -1
- package/src/context.ts +0 -54
package/src/scope.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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 {
|
|
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:
|
|
19
|
-
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
protected context: Context | null = null;
|
|
103
|
+
/** 执行器上下文 */
|
|
104
|
+
protected evaluator: Evaluator | null = null;
|
|
103
105
|
/** 求值计数 */
|
|
104
106
|
protected evalCounter = 0;
|
|
105
|
-
/**
|
|
106
|
-
reset(
|
|
107
|
-
const
|
|
107
|
+
/** 重置执行器 */
|
|
108
|
+
reset(evaluator: Evaluator | null): () => void {
|
|
109
|
+
const oldEvaluator = this.evaluator;
|
|
108
110
|
const oldCounter = this.evalCounter;
|
|
109
|
-
this.
|
|
111
|
+
this.evaluator = evaluator;
|
|
110
112
|
this.evalCounter = 0;
|
|
111
113
|
return () => {
|
|
112
|
-
this.
|
|
114
|
+
this.evaluator = oldEvaluator;
|
|
113
115
|
this.evalCounter = oldCounter;
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
118
|
/** 函数的执行环境,惰性求值 */
|
|
117
|
-
protected _proxy
|
|
119
|
+
protected _proxy: VmContext | null = null;
|
|
118
120
|
/** 函数的执行环境,惰性求值 */
|
|
119
121
|
get proxy(): VmContext {
|
|
120
|
-
this._proxy
|
|
121
|
-
|
|
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):
|
|
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 >=
|
|
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)
|
|
205
|
+
result = expression(this, this.evaluator!);
|
|
164
206
|
} else {
|
|
165
|
-
const {
|
|
166
|
-
|
|
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(
|
|
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,
|
|
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 {
|
|
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
|
|
52
|
-
|
|
53
|
-
type
|
|
54
|
-
|
|
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
|
|
86
|
+
return toBoolean as Converters[T];
|
|
59
87
|
case 'f':
|
|
60
88
|
case 'number':
|
|
61
|
-
return
|
|
89
|
+
return toNumber as Converters[T];
|
|
62
90
|
case 's':
|
|
63
91
|
case 'string':
|
|
64
|
-
return
|
|
92
|
+
return toString as Converters[T];
|
|
65
93
|
default:
|
|
66
|
-
(type) satisfies
|
|
67
|
-
return
|
|
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
|
-
|
|
4
|
+
e.import({ w: 123 });
|
|
5
|
+
const s = new Scope({ sin: 12 }, false);
|
|
5
6
|
|
|
6
|
-
describe
|
|
7
|
+
describe('Analyze', () => {
|
|
7
8
|
it('simple', () => {
|
|
8
|
-
expect(e.analyze(Expression('
|
|
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'
|
|
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
|
});
|
package/tests/compile.ts
ADDED
|
@@ -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
|
+
});
|
package/tests/condition.ts
CHANGED
|
@@ -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
|
|
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(
|
|
46
|
-
|
|
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
|
});
|