@cloudpss/expression 0.6.0-alpha.1 → 0.6.0-alpha.11
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 +67 -33
- package/dist/analyze.js.map +1 -1
- package/dist/definitions/argument.d.ts +2 -10
- 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-decoration.d.ts +3 -3
- package/dist/definitions/parameter-decoration.d.ts.map +1 -1
- package/dist/definitions/parameter-decoration.js +0 -7
- package/dist/definitions/parameter-decoration.js.map +1 -1
- package/dist/definitions/parameter-group.d.ts +0 -4
- package/dist/definitions/parameter-group.d.ts.map +1 -1
- package/dist/definitions/parameter-group.js +5 -17
- package/dist/definitions/parameter-group.js.map +1 -1
- package/dist/definitions/parameter.d.ts +47 -40
- package/dist/definitions/parameter.d.ts.map +1 -1
- package/dist/definitions/parameter.js +9 -21
- package/dist/definitions/parameter.js.map +1 -1
- package/dist/definitions/utils.d.ts +19 -0
- package/dist/definitions/utils.d.ts.map +1 -0
- package/dist/definitions/utils.js +177 -0
- package/dist/definitions/utils.js.map +1 -0
- package/dist/definitions/variable.d.ts +0 -2
- package/dist/definitions/variable.d.ts.map +1 -1
- package/dist/definitions/variable.js +1 -5
- package/dist/definitions/variable.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +1 -0
- 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 +4 -4
- 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 +145 -141
- 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 +44 -24
- package/dist/migrator/access.js.map +1 -1
- package/dist/migrator/call.d.ts.map +1 -1
- package/dist/migrator/call.js +254 -217
- 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/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.js +1 -1
- package/dist/migrator/node.js.map +1 -1
- package/dist/migrator/operator.d.ts.map +1 -1
- package/dist/migrator/operator.js +43 -29
- package/dist/migrator/operator.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 +1 -2
- package/dist/migrator/state.js.map +1 -1
- package/dist/migrator/symbol.d.ts.map +1 -1
- package/dist/migrator/symbol.js +13 -0
- package/dist/migrator/symbol.js.map +1 -1
- package/dist/migrator/to-type.d.ts.map +1 -1
- package/dist/migrator/to-type.js +20 -3
- package/dist/migrator/to-type.js.map +1 -1
- package/dist/migrator/utils.d.ts +4 -0
- package/dist/migrator/utils.d.ts.map +1 -1
- package/dist/migrator/utils.js +25 -0
- 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 +25 -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 +13 -16
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +56 -49
- package/dist/scope.js.map +1 -1
- package/dist/type.d.ts +14 -5
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +35 -11
- package/dist/type.js.map +1 -1
- package/package.json +7 -4
- package/src/analyze.ts +77 -37
- package/src/definitions/argument.ts +2 -12
- package/src/definitions/constraint.ts +3 -3
- package/src/definitions/parameter-decoration.ts +3 -9
- package/src/definitions/parameter-group.ts +4 -19
- package/src/definitions/parameter.ts +62 -61
- package/src/definitions/utils.ts +175 -0
- package/src/definitions/variable.ts +1 -6
- package/src/definitions.ts +1 -0
- package/src/eval.ts +13 -26
- package/src/expression.ts +14 -6
- package/src/index.ts +3 -1
- package/src/interface.ts +35 -0
- package/src/main.ts +213 -194
- package/src/migrate.ts +30 -6
- package/src/migrator/access.ts +49 -32
- package/src/migrator/call.ts +246 -203
- package/src/migrator/concat.ts +15 -2
- package/src/migrator/interface.ts +3 -1
- package/src/migrator/node.ts +1 -1
- package/src/migrator/operator.ts +47 -33
- package/src/migrator/state.ts +2 -2
- package/src/migrator/symbol.ts +13 -0
- package/src/migrator/to-type.ts +22 -3
- package/src/migrator/utils.ts +29 -0
- package/src/parser.ts +27 -8
- package/src/re-exports.ts +28 -0
- package/src/scope.ts +75 -61
- package/src/type.ts +32 -11
- package/tests/analyze.ts +40 -6
- package/tests/compile.ts +65 -0
- package/tests/condition.ts +13 -5
- package/tests/definition.ts +205 -18
- package/tests/eval-complex.ts +7 -10
- package/tests/eval.ts +59 -12
- package/tests/import.ts +18 -4
- package/tests/main.ts +9 -0
- package/tests/migrate.ts +202 -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/jest.config.js +0 -3
- package/src/context.ts +0 -54
- package/tests/tsconfig.json +0 -3
- package/tsconfig.json +0 -3
package/src/main.ts
CHANGED
|
@@ -1,116 +1,198 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { isVmArray, wrapToVmValue, type VmValue } from '@mirascript/mirascript';
|
|
1
|
+
import { isVmFunction, isVmPrimitive, wrapToVmValue, type VmValue } from '@mirascript/mirascript';
|
|
3
2
|
import { lib } from '@mirascript/mirascript/subtle';
|
|
4
|
-
import {
|
|
5
|
-
import { Context, type Options } from './context.js';
|
|
3
|
+
import { defaultOptions, type Logger, type Options } from './interface.js';
|
|
6
4
|
import { parse } from './parser.js';
|
|
7
|
-
import { analyze } from './analyze.js';
|
|
5
|
+
import { analyze, type AccessChain } from './analyze.js';
|
|
8
6
|
import {
|
|
9
7
|
type CompiledExpression,
|
|
10
8
|
Expression,
|
|
9
|
+
type ExpressionFunction,
|
|
11
10
|
type ExpressionOrValue,
|
|
11
|
+
type ExpressionSource,
|
|
12
12
|
ExpressionTag,
|
|
13
13
|
isExpression,
|
|
14
14
|
} from './expression.js';
|
|
15
15
|
import { evaluate } from './eval.js';
|
|
16
16
|
import { TypeInfo } from './type.js';
|
|
17
17
|
import { Scope } from './scope.js';
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
import {
|
|
19
|
+
enumType,
|
|
20
|
+
fillArgumentMap,
|
|
21
|
+
toArgumentValue,
|
|
22
|
+
type ArgumentMap,
|
|
23
|
+
type ArgumentValue,
|
|
24
|
+
type Choice,
|
|
25
|
+
type ChoiceParameterType,
|
|
26
|
+
type ConditionExpression,
|
|
27
|
+
type Parameter,
|
|
28
|
+
type ParameterGroup,
|
|
29
|
+
type ParameterMap,
|
|
28
30
|
} from './definitions.js';
|
|
29
31
|
const { isArray } = Array;
|
|
30
|
-
const {
|
|
32
|
+
const { defineProperty } = Object;
|
|
33
|
+
const { to_string } = lib;
|
|
34
|
+
|
|
35
|
+
/** 选项类型 */
|
|
36
|
+
export type EvaluatedChoices<T extends ChoiceParameterType> = {
|
|
37
|
+
[K in T]: ParameterMap[K] extends { choices?: infer C } ? (C extends readonly unknown[] ? C : never) : never;
|
|
38
|
+
}[T];
|
|
31
39
|
|
|
32
40
|
/**
|
|
33
41
|
* 表达式求值
|
|
34
42
|
*/
|
|
35
43
|
export class Evaluator {
|
|
36
|
-
constructor(options?: Partial<Options>) {
|
|
37
|
-
this.
|
|
38
|
-
}
|
|
39
|
-
/** 选项 */
|
|
40
|
-
get options(): Options {
|
|
41
|
-
return this.context.options;
|
|
44
|
+
constructor(options?: Partial<Options> | null) {
|
|
45
|
+
this.options = options ?? defaultOptions;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
48
|
+
/** Logger */
|
|
49
|
+
get logger(): Logger {
|
|
50
|
+
return this.options.logger ?? console;
|
|
51
|
+
}
|
|
52
|
+
/** 选项 */
|
|
53
|
+
readonly options: Readonly<Partial<Options>>;
|
|
46
54
|
|
|
47
55
|
/** 默认执行环境 */
|
|
48
|
-
private readonly scope = new Scope(
|
|
56
|
+
private readonly scope = new Scope(null, true, '<evaluator>');
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
private readonly __imported = new Map<string, VmValue>();
|
|
59
|
+
/** 执行环境 */
|
|
60
|
+
get imported(): ReadonlyMap<string, VmValue> {
|
|
61
|
+
return this.__imported;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 导入到执行环境
|
|
65
|
+
*
|
|
66
|
+
* 使用 `undefined` 删除已导入的变量
|
|
67
|
+
*/
|
|
51
68
|
import(values: Record<string, unknown>): void {
|
|
52
69
|
for (const [key, value] of Object.entries(values)) {
|
|
53
70
|
if (value === undefined) {
|
|
54
|
-
this.
|
|
71
|
+
this.__imported.delete(key);
|
|
55
72
|
} else {
|
|
56
|
-
this.
|
|
73
|
+
this.__imported.set(key, wrapToVmValue(value, null));
|
|
57
74
|
}
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
/**
|
|
62
|
-
* 编译表达式
|
|
63
|
-
*/
|
|
78
|
+
/** 编译表达式 */
|
|
64
79
|
compile<T extends VmValue>(expression: Expression<T>, throws = true): CompiledExpression<T> {
|
|
65
|
-
const value = parse(this
|
|
80
|
+
const value = parse(this, expression.source, throws, false);
|
|
66
81
|
const tag = expression[ExpressionTag];
|
|
67
|
-
const type = tag ? TypeInfo.parse(tag) :
|
|
68
|
-
const compiled = (
|
|
69
|
-
const ret = evaluate(
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
const type = tag ? TypeInfo.parse(tag) : '';
|
|
83
|
+
const compiled: ExpressionFunction<T> = (scope, evaluator) => {
|
|
84
|
+
const ret = evaluate<T>(evaluator, value, scope);
|
|
85
|
+
if (ret == null) return null;
|
|
86
|
+
if (!type) return ret;
|
|
87
|
+
return TypeInfo.to(ret, type) as T;
|
|
88
|
+
};
|
|
89
|
+
defineProperty(compiled, ExpressionTag, { value: type, enumerable: true });
|
|
90
|
+
defineProperty(compiled, 'source', { value: value.source, enumerable: true });
|
|
91
|
+
return compiled as CompiledExpression<T>;
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
/** 执行 MiraScript 模板 */
|
|
95
|
+
template(template: string | null | undefined, scope: Scope | undefined): string {
|
|
96
|
+
if (template == null) return '';
|
|
97
|
+
const throws = scope?.throws ?? true;
|
|
98
|
+
const value = parse(this, template, throws, true);
|
|
99
|
+
const result = evaluate<string>(this, value, scope ?? this.scope) ?? template;
|
|
100
|
+
if (typeof result == 'string') return result;
|
|
101
|
+
return to_string(result);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 求值 */
|
|
105
|
+
evaluate<T extends VmValue>(
|
|
106
|
+
expression: ExpressionOrValue<T> | ExpressionFunction<T> | null | undefined,
|
|
107
|
+
scope?: Scope | null,
|
|
108
|
+
): T | null;
|
|
109
|
+
/** 求值 */
|
|
110
|
+
evaluate<T extends VmValue, const D = NonNullable<T>>(
|
|
111
|
+
expression: ExpressionOrValue<T> | ExpressionFunction<T> | null | undefined,
|
|
112
|
+
scope: Scope | null | undefined,
|
|
87
113
|
defaults: D,
|
|
88
|
-
): T | D;
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
evaluate<T
|
|
93
|
-
expression:
|
|
94
|
-
scope: Scope | undefined,
|
|
95
|
-
defaults
|
|
96
|
-
): T | D
|
|
114
|
+
): NonNullable<T> | D;
|
|
115
|
+
/** 求值 */
|
|
116
|
+
evaluate<T>(expression: ExpressionFunction<T> | T | null | undefined, scope?: Scope | null): T | null;
|
|
117
|
+
/** 求值 */
|
|
118
|
+
evaluate<T, const D = NonNullable<T>>(
|
|
119
|
+
expression: ExpressionFunction<T> | T | null | undefined,
|
|
120
|
+
scope: Scope | null | undefined,
|
|
121
|
+
defaults: D,
|
|
122
|
+
): NonNullable<T> | D;
|
|
123
|
+
|
|
124
|
+
/** 求值 */
|
|
125
|
+
evaluate<T extends VmValue, const D = NonNullable<T>>(
|
|
126
|
+
expression: ExpressionOrValue<T> | ExpressionFunction<T> | null | undefined,
|
|
127
|
+
scope: Scope | null | undefined,
|
|
128
|
+
defaults: D | null = null,
|
|
129
|
+
): T | D | null {
|
|
97
130
|
if (expression == null) return defaults;
|
|
98
|
-
|
|
99
|
-
|
|
131
|
+
|
|
132
|
+
if (typeof expression == 'function' && !isVmFunction(expression)) {
|
|
133
|
+
try {
|
|
134
|
+
const ret = expression(scope ?? this.scope, this);
|
|
135
|
+
if (ret == null) return defaults;
|
|
136
|
+
return ret;
|
|
137
|
+
} catch (ex) {
|
|
138
|
+
if (scope?.throws ?? true) {
|
|
139
|
+
throw ex;
|
|
140
|
+
}
|
|
141
|
+
return defaults;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!isExpression<T>(expression)) {
|
|
146
|
+
return expression;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const result = this.evaluateSource(expression.source, scope);
|
|
150
|
+
if (result == null) return defaults;
|
|
151
|
+
|
|
100
152
|
const tag = expression[ExpressionTag];
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
153
|
+
if (!tag) return result;
|
|
154
|
+
return TypeInfo.to(result, tag) as T;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** 求值 */
|
|
158
|
+
evaluateSource<T extends VmValue>(
|
|
159
|
+
expression: ExpressionSource<T> | T | null | undefined,
|
|
160
|
+
scope: Scope | null | undefined,
|
|
161
|
+
): T | null;
|
|
162
|
+
/** 求值 */
|
|
163
|
+
evaluateSource<T extends VmValue, const D = NonNullable<T>>(
|
|
164
|
+
expression: ExpressionSource<T> | T | null | undefined,
|
|
165
|
+
scope: Scope | null | undefined,
|
|
166
|
+
defaults: D,
|
|
167
|
+
): NonNullable<T> | D;
|
|
168
|
+
|
|
169
|
+
/** 求值 */
|
|
170
|
+
evaluateSource<T extends VmValue, const D = NonNullable<T>>(
|
|
171
|
+
expression: ExpressionSource<T> | T | null | undefined,
|
|
172
|
+
scope: Scope | null | undefined,
|
|
173
|
+
defaults: D | null = null,
|
|
174
|
+
): NonNullable<T> | D | null {
|
|
175
|
+
if (expression == null) return defaults;
|
|
176
|
+
if (typeof expression != 'string') return expression;
|
|
177
|
+
|
|
178
|
+
scope ??= this.scope;
|
|
179
|
+
const { throws } = scope;
|
|
180
|
+
const value = parse(this, expression, throws, false);
|
|
181
|
+
const result = evaluate<T>(this, value, scope);
|
|
182
|
+
if (result == null) return defaults;
|
|
183
|
+
|
|
104
184
|
return result;
|
|
105
185
|
}
|
|
106
186
|
|
|
107
187
|
/** 求条件 */
|
|
108
|
-
evaluateCondition(
|
|
188
|
+
evaluateCondition(
|
|
189
|
+
condition: ConditionExpression | null | undefined,
|
|
190
|
+
scope: Scope | null | undefined,
|
|
191
|
+
defaults = true,
|
|
192
|
+
): boolean {
|
|
109
193
|
if (typeof condition == 'boolean') return condition;
|
|
110
194
|
if (condition == null) return defaults;
|
|
111
|
-
condition = (
|
|
112
|
-
typeof condition == 'string' ? condition : lib.to_string(condition)
|
|
113
|
-
).trim() as ConditionExpression & string;
|
|
195
|
+
condition = to_string(condition).trim() as ConditionExpression & string;
|
|
114
196
|
if (condition === '') return defaults;
|
|
115
197
|
if (condition === 'true') return true;
|
|
116
198
|
if (condition === 'false') return false;
|
|
@@ -120,92 +202,83 @@ export class Evaluator {
|
|
|
120
202
|
if (scope?.throws) {
|
|
121
203
|
throw ex;
|
|
122
204
|
}
|
|
123
|
-
this.
|
|
205
|
+
this.logger.warn(`Invalid condition`, ex);
|
|
124
206
|
return defaults;
|
|
125
207
|
}
|
|
126
208
|
}
|
|
127
209
|
|
|
128
210
|
/** 求选项列表 */
|
|
129
|
-
evaluateChoices<
|
|
130
|
-
|
|
131
|
-
scope: Scope | undefined,
|
|
132
|
-
):
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
211
|
+
evaluateChoices<T extends ChoiceParameterType>(
|
|
212
|
+
definition: Pick<ParameterMap[T], 'type' | 'choices'>,
|
|
213
|
+
scope: Scope | null | undefined,
|
|
214
|
+
): EvaluatedChoices<T> {
|
|
215
|
+
const ret: Choice[] = [];
|
|
216
|
+
const { type, choices } = definition;
|
|
217
|
+
if (choices != null) {
|
|
218
|
+
const result = this.evaluate(choices, scope);
|
|
219
|
+
if (result == null) {
|
|
220
|
+
// do nothing
|
|
221
|
+
} else if (!isArray(result as unknown[])) {
|
|
222
|
+
if (scope?.throws ?? true) {
|
|
223
|
+
throw new Error(`Choices is not an array`);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const len = result.length;
|
|
227
|
+
for (let i = 0; i < len; i++) {
|
|
228
|
+
const item = result[i];
|
|
229
|
+
if (item == null) {
|
|
230
|
+
ret[i] = { key: i, name: String(i), description: '', condition: undefined };
|
|
231
|
+
} else if (isVmPrimitive(item)) {
|
|
232
|
+
ret[i] = { key: item, name: String(item), description: '', condition: undefined };
|
|
233
|
+
} else {
|
|
234
|
+
const key = item.key ?? i;
|
|
235
|
+
ret[i] = {
|
|
236
|
+
key,
|
|
237
|
+
name: item.name ?? String(key),
|
|
238
|
+
description: item.description ?? '',
|
|
239
|
+
condition: item.condition || undefined,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
139
243
|
}
|
|
140
|
-
return [];
|
|
141
244
|
}
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** 转换参数值的类型 */
|
|
159
|
-
toArg<V extends ArgumentValue>(value: VmValue, definition: Parameter): V {
|
|
160
|
-
switch (definition.type as LiteralUnion<keyof ParameterMap, string>) {
|
|
161
|
-
case 'real':
|
|
162
|
-
case 'integer':
|
|
163
|
-
return to_number(value) as V;
|
|
164
|
-
case 'file':
|
|
165
|
-
case 'text':
|
|
166
|
-
case 'code':
|
|
167
|
-
case 'pinLike':
|
|
168
|
-
case 'cssColor':
|
|
169
|
-
case 'cssBackground':
|
|
170
|
-
case 'resourceId':
|
|
171
|
-
return to_string(value) as V;
|
|
172
|
-
|
|
173
|
-
case 'choice':
|
|
174
|
-
case 'logical':
|
|
175
|
-
return value as V;
|
|
176
|
-
|
|
177
|
-
case 'multiSelect':
|
|
178
|
-
case 'table': {
|
|
179
|
-
if (isVmArray(value)) {
|
|
180
|
-
return value as V;
|
|
245
|
+
if (type === 'logical') {
|
|
246
|
+
if (ret.length >= 2) {
|
|
247
|
+
ret.length = 2;
|
|
248
|
+
} else {
|
|
249
|
+
const type = enumType(definition) ?? 'boolean';
|
|
250
|
+
const keys = type === 'boolean' ? [false, true] : type === 'number' ? [0, 1] : ['0', '1'];
|
|
251
|
+
while (ret.length < 2) {
|
|
252
|
+
ret.push({
|
|
253
|
+
key: keys[ret.length]!,
|
|
254
|
+
name: '',
|
|
255
|
+
description: '',
|
|
256
|
+
condition: undefined,
|
|
257
|
+
});
|
|
181
258
|
}
|
|
182
|
-
return (value != null ? [value] : []) as ArgumentValue as V;
|
|
183
259
|
}
|
|
184
|
-
case 'grouped':
|
|
185
|
-
case 'record':
|
|
186
|
-
default:
|
|
187
|
-
return value as V;
|
|
188
260
|
}
|
|
261
|
+
return ret as EvaluatedChoices<T>;
|
|
189
262
|
}
|
|
190
263
|
|
|
191
264
|
/** 求参数值 */
|
|
192
265
|
evaluateArg<V extends ArgumentValue>(
|
|
193
|
-
expression: ExpressionOrValue<V> | undefined,
|
|
194
|
-
scope: Scope | undefined,
|
|
266
|
+
expression: ExpressionOrValue<V> | null | undefined,
|
|
267
|
+
scope: Scope | null | undefined,
|
|
195
268
|
definition: Parameter,
|
|
196
269
|
): V {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return
|
|
270
|
+
// 默认值有可能为表达式,因此需要先填充再求值
|
|
271
|
+
expression ??= definition.value as ExpressionOrValue<V>;
|
|
272
|
+
const result = this.evaluate<V>(expression, scope, definition.value as V);
|
|
273
|
+
return toArgumentValue<V>(result, definition);
|
|
201
274
|
}
|
|
202
275
|
/** 求参数表的所有参数值 */
|
|
203
276
|
evaluateArgs<T extends Record<string, ArgumentValue>>(
|
|
204
|
-
args: ArgumentMap<T> | undefined,
|
|
205
|
-
scope: Scope | undefined,
|
|
277
|
+
args: ArgumentMap<T> | null | undefined,
|
|
278
|
+
scope: Scope | null | undefined,
|
|
206
279
|
definition: ReadonlyArray<Pick<ParameterGroup, 'items'>>,
|
|
207
280
|
): T {
|
|
208
|
-
const a =
|
|
281
|
+
const a = fillArgumentMap(definition, args);
|
|
209
282
|
const ret: Record<string, ArgumentValue> = {};
|
|
210
283
|
for (const group of definition) {
|
|
211
284
|
for (const param of group.items) {
|
|
@@ -217,63 +290,6 @@ export class Evaluator {
|
|
|
217
290
|
}
|
|
218
291
|
return ret as T;
|
|
219
292
|
}
|
|
220
|
-
/** 获取参数默认值,修复现有的参数,并填充缺失的参数 */
|
|
221
|
-
defaultArgs<T extends Record<string, ArgumentValue>>(
|
|
222
|
-
definition: ReadonlyArray<Pick<ParameterGroup, 'items'>> | undefined,
|
|
223
|
-
args: ArgumentMap<T> = {} as ArgumentMap<T>,
|
|
224
|
-
): ArgumentMap<T> {
|
|
225
|
-
if (definition == null || !isArray(definition as unknown[])) {
|
|
226
|
-
return args;
|
|
227
|
-
}
|
|
228
|
-
/** 参数键的类型 */
|
|
229
|
-
type K = keyof T & string;
|
|
230
|
-
/** 参数值的类型 */
|
|
231
|
-
type V = NonNullable<T[K]>;
|
|
232
|
-
const keys = new Set(Object.keys(args) as K[]);
|
|
233
|
-
|
|
234
|
-
// 根据定义填充或修复参数
|
|
235
|
-
for (const group of definition) {
|
|
236
|
-
for (const param of group.items) {
|
|
237
|
-
const key = param.key as K;
|
|
238
|
-
if (!key) continue;
|
|
239
|
-
const currentValue = keys.has(key) ? args[key] : undefined;
|
|
240
|
-
if (currentValue == null) {
|
|
241
|
-
// 使用默认值填充
|
|
242
|
-
const defaultValue = param.value;
|
|
243
|
-
if (isExpression(defaultValue)) {
|
|
244
|
-
args[key] = { [ExpressionTag]: '', source: defaultValue.source } as Expression<V>;
|
|
245
|
-
} else {
|
|
246
|
-
const value =
|
|
247
|
-
defaultValue != null && typeof defaultValue == 'object'
|
|
248
|
-
? klona(defaultValue)
|
|
249
|
-
: defaultValue;
|
|
250
|
-
args[key] = this.toArg<V>(value, param);
|
|
251
|
-
}
|
|
252
|
-
} else {
|
|
253
|
-
// 已存在的参数值
|
|
254
|
-
if (isExpression(currentValue)) {
|
|
255
|
-
// 保留表达式
|
|
256
|
-
} else {
|
|
257
|
-
// 修复现有的参数值
|
|
258
|
-
const castedValue = this.toArg<V>(currentValue, param);
|
|
259
|
-
if (castedValue != null && castedValue !== currentValue) {
|
|
260
|
-
args[key] = castedValue;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
keys.delete(key);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// 删除多余的参数
|
|
269
|
-
for (const key of keys) {
|
|
270
|
-
// 保留特殊参数和私有字段
|
|
271
|
-
if (key.startsWith('_') || key.startsWith('$') || key.startsWith('@')) continue;
|
|
272
|
-
delete args[key];
|
|
273
|
-
}
|
|
274
|
-
return args;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
293
|
/** 解析表达式 */
|
|
278
294
|
validate<T extends VmValue>(expression: Expression<T>): void {
|
|
279
295
|
if (!isExpression(expression) || !expression.source) {
|
|
@@ -281,15 +297,18 @@ export class Evaluator {
|
|
|
281
297
|
}
|
|
282
298
|
const tag = expression[ExpressionTag];
|
|
283
299
|
if (tag) TypeInfo.parse(tag);
|
|
284
|
-
parse(this
|
|
300
|
+
parse(this, expression.source, true, false);
|
|
285
301
|
}
|
|
286
302
|
|
|
287
303
|
/** 分析表达式 */
|
|
288
|
-
analyze<T extends VmValue>(expression: Expression<T
|
|
304
|
+
analyze<T extends VmValue>(expression: Expression<T>, scope?: Scope | null): readonly AccessChain[] {
|
|
289
305
|
if (!isExpression(expression) || !expression.source) {
|
|
290
306
|
throw new Error(`${expression.source || String(expression)} is not a valid expression`);
|
|
291
307
|
}
|
|
292
|
-
|
|
293
|
-
|
|
308
|
+
scope ??= this.scope;
|
|
309
|
+
const exp = parse(this, expression.source, scope.throws, false);
|
|
310
|
+
// 如果 throws=true,则 parse 会抛出错误,此处有 error 一定是 throws=false 的情况
|
|
311
|
+
if (exp.error) return [];
|
|
312
|
+
return analyze(exp, this, scope);
|
|
294
313
|
}
|
|
295
314
|
}
|
package/src/migrate.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { VmValue } from '@mirascript/mirascript';
|
|
2
|
+
import type { Scope } from './scope.js';
|
|
3
3
|
import type { ExpressionSource } from './expression.js';
|
|
4
|
+
import { BaseState } from './migrator/state.js';
|
|
5
|
+
import { Evaluator } from './main.js';
|
|
6
|
+
import { DefaultVmContext } from '@mirascript/mirascript/subtle';
|
|
4
7
|
|
|
8
|
+
const evaluator = new Evaluator();
|
|
5
9
|
/** MathJs 表达式转为 MiraScript 表达式 */
|
|
6
10
|
export function migrateMathJs<const T extends VmValue>(
|
|
7
11
|
expr: ExpressionSource<T>,
|
|
8
12
|
condition: boolean,
|
|
9
|
-
context
|
|
13
|
+
context: Scope | undefined,
|
|
10
14
|
): ExpressionSource<T> {
|
|
11
15
|
// 检查输入
|
|
12
16
|
if (!expr) return '' as ExpressionSource<T>;
|
|
@@ -15,11 +19,31 @@ export function migrateMathJs<const T extends VmValue>(
|
|
|
15
19
|
if (expr === 'null') return 'nil' as ExpressionSource<T>;
|
|
16
20
|
if (expr === 'true') return 'true' as ExpressionSource<T>;
|
|
17
21
|
if (expr === 'false') return 'false' as ExpressionSource<T>;
|
|
22
|
+
if (condition) {
|
|
23
|
+
// 兼容旧的 evaluateCondition 的宽容处理
|
|
24
|
+
if (expr === 'True' || expr === 'TRUE' || expr === '1' || expr === '"1"' || expr === "'1'")
|
|
25
|
+
return 'true' as ExpressionSource<T>;
|
|
26
|
+
if (
|
|
27
|
+
expr === 'False' ||
|
|
28
|
+
expr === 'FALSE' ||
|
|
29
|
+
expr === '0' ||
|
|
30
|
+
expr === '"0"' ||
|
|
31
|
+
expr === "'0'" ||
|
|
32
|
+
expr === '""' ||
|
|
33
|
+
expr === "''"
|
|
34
|
+
)
|
|
35
|
+
return 'false' as ExpressionSource<T>;
|
|
36
|
+
}
|
|
18
37
|
if (/^[-+]?(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)$/.test(expr)) {
|
|
19
38
|
return expr;
|
|
20
39
|
}
|
|
21
40
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
const dispose = context?.reset(evaluator);
|
|
42
|
+
try {
|
|
43
|
+
const state = new BaseState(expr, condition, context ? context.proxy : DefaultVmContext);
|
|
44
|
+
state.migrate();
|
|
45
|
+
return state.result() as ExpressionSource<T>;
|
|
46
|
+
} finally {
|
|
47
|
+
dispose?.();
|
|
48
|
+
}
|
|
25
49
|
}
|