@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
@@ -1,6 +1,6 @@
1
1
  import type { VmPrimitive, VmRecord } from '@mirascript/mirascript';
2
- import type { ExpressionOrValue } from '../expression.js';
3
- import type { ArgumentMap, ArgumentValue, ConditionExpression, ParameterFunction } from './argument.js';
2
+ import type { ExpressionFunction, ExpressionOrValue } from '../expression.js';
3
+ import type { ArgumentMap, ArgumentValue, ConditionExpression } from './argument.js';
4
4
  import type { Constraint } from './constraint.js';
5
5
  import type { ParameterDecoration } from './parameter-decoration.js';
6
6
  import type { ParameterGroup } from './parameter-group.js';
@@ -65,13 +65,10 @@ export interface IntegerParameter extends NumericParameter<'integer'> {}
65
65
  /** 可用于选项的值 */
66
66
  export type ChoiceArgumentValue = NonNullable<VmPrimitive>;
67
67
 
68
- /** 表示一个选项 Key,当其值为数字时,默认值为其在数组中的索引 */
69
- export type ChoiceKey<V extends ChoiceArgumentValue = ChoiceArgumentValue> = V extends number ? number | null : V;
70
-
71
68
  /** 表示一个选项 */
72
69
  export type Choice<V extends ChoiceArgumentValue = ChoiceArgumentValue> = {
73
- /** 选中该选项将存储的参数值 */
74
- key: ChoiceKey<V>;
70
+ /** 选中该选项将存储的参数值,默认值为其在数组中的索引 */
71
+ key: V;
75
72
  /** 选项名称 */
76
73
  name: string;
77
74
  /** 选项详细描述 */
@@ -84,47 +81,60 @@ export type Choice<V extends ChoiceArgumentValue = ChoiceArgumentValue> = {
84
81
  export function isChoice<V extends ChoiceArgumentValue = ChoiceArgumentValue>(value: unknown): value is Choice<V> {
85
82
  if (value == null || typeof value != 'object') return false;
86
83
  const c = value as Choice<V>;
87
- return typeof c.name === 'string' && typeof c.description == 'string' && !('items' in c) && !('value' in c);
84
+ return (
85
+ typeof c.name === 'string' &&
86
+ typeof c.description == 'string' &&
87
+ !('items' in c) &&
88
+ !('value' in c) &&
89
+ !('span' in c)
90
+ );
88
91
  }
89
92
  /**
90
93
  * 表示值为选择项的参数定义
91
94
  */
92
- export interface ChoiceParameter<V extends ChoiceArgumentValue = ChoiceArgumentValue>
93
- extends ParameterBase<'choice', V> {
95
+ export interface ChoiceParameter<V extends ChoiceArgumentValue = ChoiceArgumentValue> extends ParameterBase<
96
+ 'choice',
97
+ V
98
+ > {
94
99
  /**
95
100
  * 可选取值及其对应的说明文本
96
101
  */
97
- choices: ExpressionOrValue<Array<Choice<V>>> | ParameterFunction<Array<Choice<V>>>;
102
+ choices: ExpressionOrValue<Array<Choice<V>>> | ExpressionFunction<ReadonlyArray<Choice<V>>>;
98
103
  }
99
104
  /**
100
105
  * 表示值为多选选择项的参数定义
101
106
  */
102
- export interface MultiSelectParameter<V extends readonly ChoiceArgumentValue[] = readonly ChoiceArgumentValue[]>
103
- extends ParameterBase<'multiSelect', V> {
107
+ export interface MultiSelectParameter<
108
+ V extends readonly ChoiceArgumentValue[] = readonly ChoiceArgumentValue[],
109
+ > extends ParameterBase<'multiSelect', V> {
104
110
  /**
105
111
  * 可选取值及其对应的说明文本
106
112
  */
107
- choices: ExpressionOrValue<Array<Choice<V[number]>>> | ParameterFunction<Array<Choice<V[number]>>>;
113
+ choices: ExpressionOrValue<Array<Choice<V[number]>>> | ExpressionFunction<ReadonlyArray<Choice<V[number]>>>;
108
114
  }
109
115
 
110
116
  /**
111
117
  * 表示值为布尔量的参数定义
112
118
  */
113
- export interface LogicalParameter<V extends ChoiceArgumentValue = ChoiceArgumentValue>
114
- extends ParameterBase<'logical', V> {
119
+ export interface LogicalParameter<V extends ChoiceArgumentValue = ChoiceArgumentValue> extends ParameterBase<
120
+ 'logical',
121
+ V
122
+ > {
115
123
  /**
116
124
  * 可选取值及其对应的说明文本
117
125
  */
118
126
  choices?:
119
127
  | ExpressionOrValue<[false: Choice<V>, true: Choice<V>]>
120
- | ParameterFunction<[false: Choice<V>, true: Choice<V>]>;
128
+ | ExpressionFunction<readonly [false: Choice<V>, true: Choice<V>]>;
121
129
  }
122
130
 
123
131
  /**
124
132
  * 表示值为表格的参数定义
125
133
  */
126
- export interface TableParameter<V extends readonly ArgumentValue[] = readonly ArgumentValue[]>
127
- extends ParameterBase<'table', V> {
134
+ export interface TableParameter<V extends readonly ArgumentValue[] = readonly ArgumentValue[]> extends ParameterBase<
135
+ 'table',
136
+ V
137
+ > {
128
138
  /** 最小行数 */
129
139
  minRowCount?: number;
130
140
  /** 最大行数 */
@@ -138,7 +148,15 @@ export interface TableParameter<V extends readonly ArgumentValue[] = readonly Ar
138
148
  *
139
149
  * 否则,表格参数的值为记录类型数组,每个记录包含各列定义的键值
140
150
  */
141
- columns: Parameter[] | ParameterFunction<Parameter[]>;
151
+ columns: Parameter[] | ExpressionFunction<readonly Parameter[]>;
152
+ /** 添加额外属性以跟踪表格行 */
153
+ trackTag?: string;
154
+ }
155
+
156
+ /** 生成用于跟踪的额外属性值 */
157
+ export function generateTrackId(): number {
158
+ // 生成 1 ~ 2^31-1 之间的随机整数
159
+ return Math.trunc(1 + Math.random() * 0x7fff_ffff);
142
160
  }
143
161
 
144
162
  /**
@@ -219,13 +237,13 @@ export interface ParameterMap {
219
237
  /** 整数参数 */
220
238
  integer: IntegerParameter;
221
239
  /** 选择参数 */
222
- choice: ChoiceParameter<string> | ChoiceParameter<number> | ChoiceParameter<boolean>;
240
+ choice: ChoiceParameter;
223
241
  /** 多选参数 */
224
- multiSelect: MultiSelectParameter<string[]> | MultiSelectParameter<number[]> | MultiSelectParameter<boolean[]>;
242
+ multiSelect: MultiSelectParameter;
225
243
  /** 布尔参数 */
226
- logical: LogicalParameter<string> | LogicalParameter<number> | LogicalParameter<boolean>;
244
+ logical: LogicalParameter;
227
245
  /** 表格参数 */
228
- table: TableParameter<ArgumentValue[]>;
246
+ table: TableParameter;
229
247
  /** 文本参数 */
230
248
  text: TextParameter;
231
249
  /** 代码参数 */
@@ -244,6 +262,11 @@ export type ParameterType = keyof ParameterMap;
244
262
  /** 已定义的参数类型 */
245
263
  export type Parameter = ParameterMap[ParameterType];
246
264
 
265
+ /** 已定义的选择参数类型 */
266
+ export type ChoiceParameterType = {
267
+ [K in keyof ParameterMap]: ParameterMap[K] extends { choices?: infer _ } ? ParameterMap[K]['type'] : never;
268
+ }[keyof ParameterMap];
269
+
247
270
  /**
248
271
  * 是否为参数定义
249
272
  */
@@ -0,0 +1,288 @@
1
+ import { klona } from 'klona';
2
+ import { isVmArray, isVmConst, isVmRecord, type VmConst, type VmRecord, type VmValue } from '@mirascript/mirascript';
3
+ import { Expression, isExpression, type ExpressionOrValue } from '../expression.js';
4
+ import type { ChoiceParameterType, Parameter, ParameterMap } from './parameter.js';
5
+ import { TypeInfo } from '../type.js';
6
+ import type { ArgumentMap, ArgumentValue } from './argument.js';
7
+ import type { ParameterGroup } from './parameter-group.js';
8
+ const { isArray } = Array;
9
+
10
+ /** 转换参数类型 */
11
+ export type ArgumentConverter<out R extends ArgumentValue = ArgumentValue> = <const D = undefined>(
12
+ value: VmValue | null | undefined,
13
+ defaults?: D,
14
+ ) => R | Exclude<D, undefined>;
15
+
16
+ /** 回退值 */
17
+ function toFallback<F>(fallback: F | undefined): Exclude<F, undefined> {
18
+ if (fallback === undefined) {
19
+ throw new TypeError('Conversion failed and no fallback value provided');
20
+ }
21
+ return fallback as Exclude<F, undefined>;
22
+ }
23
+ /**
24
+ * 通过枚举参数定义推测枚举类型
25
+ */
26
+ export function choiceParameterType(
27
+ definition: Pick<ParameterMap[ChoiceParameterType], 'type' | 'value' | 'choices'>,
28
+ ): TypeInfo {
29
+ // 首先根据参数类型确定默认类型
30
+ let defaultType: TypeInfo = definition.type === 'logical' ? 'boolean' : 'number';
31
+ // 然后根据默认值调整默认类型
32
+ const { value, choices } = definition;
33
+ if (definition.type === 'multiSelect' && Array.isArray(value)) {
34
+ const v: unknown = value[0];
35
+ if (isChoiceValue(v)) {
36
+ defaultType = typeof v as TypeInfo;
37
+ }
38
+ } else {
39
+ if (isChoiceValue(value)) {
40
+ defaultType = typeof value as TypeInfo;
41
+ }
42
+ }
43
+
44
+ if (choices == null || typeof choices == 'function' || isExpression(choices) || !choices.length) {
45
+ return defaultType;
46
+ }
47
+
48
+ // 认为枚举选项值类型一致,推测枚举类型
49
+ for (const choice of choices) {
50
+ const v = choice?.key;
51
+ if (!isChoiceValue(v)) continue;
52
+ return typeof v as TypeInfo;
53
+ }
54
+ return defaultType;
55
+ }
56
+
57
+ /** 是否为枚举值类型 */
58
+ export function isChoiceValue(value: unknown): value is ChoiceParameterType {
59
+ return typeof value == 'string' || typeof value == 'number' || typeof value == 'boolean';
60
+ }
61
+
62
+ const defaultConverter: ArgumentConverter = (value, defaults) => {
63
+ if (value != null) return value as ArgumentValue;
64
+ return toFallback(defaults);
65
+ };
66
+
67
+ /** 转换数组参数类型 */
68
+ function createArrayConverter<T extends ArgumentValue>(
69
+ itemConverter: ArgumentConverter<T> | null = null,
70
+ ): ArgumentConverter<readonly T[]> {
71
+ if (itemConverter == null) {
72
+ return (value, defaults) => {
73
+ if (value == null) return [];
74
+ if (isVmArray(value)) return value as T[];
75
+ if (isVmConst(value)) return [value] as T[];
76
+ return toFallback(defaults);
77
+ };
78
+ }
79
+ return (value, defaults) => {
80
+ if (value == null) return [];
81
+ let arr: ArgumentValue[];
82
+ if (isVmArray(value)) arr = value as ArgumentValue[];
83
+ else if (isVmConst(value)) arr = [value];
84
+ else return toFallback(defaults);
85
+ if (arr.length === 0) return arr as T[];
86
+ let ret: T[] | null = null;
87
+ for (let i = 0; i < arr.length; i++) {
88
+ const el = arr[i];
89
+ const r = itemConverter(el, FALLBACK);
90
+ if (r === FALLBACK) return toFallback(defaults);
91
+ if (!Object.is(r, el)) {
92
+ ret ??= [...(arr as T[])];
93
+ ret[i] = r;
94
+ }
95
+ }
96
+ return ret ?? (arr as T[]);
97
+ };
98
+ }
99
+
100
+ /** 转换记录参数类型 */
101
+ function createRecordConverter<T extends Record<string, VmConst | undefined> = VmRecord>(
102
+ fieldConverters: ReadonlyArray<[key: keyof T, converter: ArgumentConverter | null]> | null = null,
103
+ ): ArgumentConverter<T> {
104
+ if (fieldConverters == null) {
105
+ return (value, defaults) => {
106
+ if (isVmRecord(value)) return value as T;
107
+ if (isVmArray(value)) {
108
+ const obj: Record<string, VmConst> = {};
109
+ for (let i = 0; i < value.length; i++) {
110
+ obj[i] = value[i] ?? null;
111
+ }
112
+ return obj as T;
113
+ }
114
+ return toFallback(defaults);
115
+ };
116
+ }
117
+ return (value, defaults) => {
118
+ let record: VmRecord;
119
+ if (isVmRecord(value)) record = value;
120
+ else if (isVmArray(value)) {
121
+ const obj: Record<string, VmConst> = {};
122
+ for (let i = 0; i < value.length; i++) {
123
+ obj[i] = value[i] ?? null;
124
+ }
125
+ record = obj;
126
+ } else return toFallback(defaults);
127
+ let ret: T | null = null;
128
+ for (const [key, converter] of fieldConverters) {
129
+ if (typeof converter != 'function') continue;
130
+ const fieldValue = record[key as string];
131
+ const r = converter(fieldValue, FALLBACK);
132
+ if (r === FALLBACK) return toFallback(defaults);
133
+ if (!Object.is(r, fieldValue)) {
134
+ ret ??= { ...(record as T) };
135
+ ret[key] = r as T[typeof key];
136
+ }
137
+ }
138
+ return ret ?? (record as T);
139
+ };
140
+ }
141
+
142
+ const FALLBACK = Symbol('FALLBACK');
143
+ /** 转换推测参数类型,不回退到 {@link Parameter.value} 提供的默认值 */
144
+ export function argumentConverter(definition: Parameter): ArgumentConverter {
145
+ switch (definition.type) {
146
+ case 'real':
147
+ case 'integer':
148
+ return TypeInfo.converter('number');
149
+ case 'text':
150
+ case 'file':
151
+ case 'code':
152
+ case 'pinLike' as never:
153
+ case 'cssColor' as never:
154
+ case 'cssBackground' as never:
155
+ case 'resourceId' as never:
156
+ return TypeInfo.converter('string');
157
+
158
+ case 'choice':
159
+ case 'logical': {
160
+ const type = choiceParameterType(definition);
161
+ return TypeInfo.converter(type);
162
+ }
163
+
164
+ case 'multiSelect': {
165
+ const type = choiceParameterType(definition);
166
+ const to = TypeInfo.converter(type);
167
+ return createArrayConverter(to);
168
+ }
169
+
170
+ case 'table': {
171
+ const { columns } = definition;
172
+ if (!isArray(columns) || !columns.length) {
173
+ return createArrayConverter();
174
+ }
175
+ if (columns.length === 1) {
176
+ const item = argumentConverter(columns[0]!);
177
+ return createArrayConverter(item);
178
+ } else {
179
+ return createArrayConverter(
180
+ createRecordConverter(
181
+ columns.map((col, index) => [col.key || String(index), argumentConverter(col)] as const),
182
+ ),
183
+ );
184
+ }
185
+ }
186
+
187
+ case 'grouped':
188
+ case 'record': {
189
+ return createRecordConverter();
190
+ }
191
+ default:
192
+ return defaultConverter;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 转换参数值的类型,不回退到 {@link Parameter.value} 提供的默认值
198
+ * 转换失败时返回 null
199
+ */
200
+ export function toArgumentValue(value: VmValue | null | undefined, definition: Parameter): ArgumentValue | null {
201
+ const converter = argumentConverter(definition);
202
+ return converter(value, null);
203
+ }
204
+
205
+ /** 参数定义数组 */
206
+ type ParameterDefinitions = ReadonlyArray<Pick<ParameterGroup, 'items'>>;
207
+ /** 是否为包含参数组的参数定义数组 */
208
+ function checkParameterDefinitions(value: ParameterDefinitions | undefined): value is ParameterDefinitions {
209
+ return value != null && isArray(value as unknown[]) && value.length > 0;
210
+ }
211
+
212
+ /** 查找指定参数 */
213
+ function findParameterImpl(items: readonly Parameter[], key: string): Parameter | undefined {
214
+ if (items.length === 0) return undefined;
215
+ for (const item of items) {
216
+ if (item == null) continue;
217
+ if (item.key === key) return item;
218
+ }
219
+ return undefined;
220
+ }
221
+
222
+ /** 查找指定参数 */
223
+ export function findParameter(definition: ParameterDefinitions | undefined, key: string): Parameter | undefined {
224
+ if (!checkParameterDefinitions(definition)) return undefined;
225
+ for (const group of definition) {
226
+ if (group == null || !isArray(group.items)) continue;
227
+ const found = findParameterImpl(group.items, key);
228
+ if (found) return found;
229
+ }
230
+ return undefined;
231
+ }
232
+
233
+ /** 获取参数默认值,修复现有的参数,填充缺失的参数,删除未定义的参数 */
234
+ export function fillArgumentMap<T extends Record<string, ArgumentValue>>(
235
+ definition: ParameterDefinitions | undefined,
236
+ args?: ArgumentMap<T> | null,
237
+ ): ArgumentMap<T> {
238
+ args ??= {} as ArgumentMap<T>;
239
+ if (!checkParameterDefinitions(definition)) return args;
240
+
241
+ /** 参数键的类型 */
242
+ type K = keyof T;
243
+ /** 参数值的类型 */
244
+ type V = NonNullable<T[K]>;
245
+ const keys = new Set(Object.keys(args) as K[]);
246
+
247
+ // 根据定义填充或修复参数
248
+ for (const group of definition) {
249
+ for (const param of group.items) {
250
+ const key = param.key as K;
251
+ if (!key) continue;
252
+ const currentValue = keys.has(key) ? args[key] : undefined;
253
+ if (currentValue == null) {
254
+ // 使用默认值填充
255
+ const defaultValue = param.value as ExpressionOrValue<V>;
256
+ if (isExpression(defaultValue)) {
257
+ args[key] = Expression<V>(defaultValue.source);
258
+ } else {
259
+ const value =
260
+ defaultValue != null && typeof defaultValue == 'object' ? klona(defaultValue) : defaultValue;
261
+ args[key] = (toArgumentValue(value, param) as V | null) ?? value;
262
+ }
263
+ } else {
264
+ // 已存在的参数值
265
+ if (isExpression(currentValue)) {
266
+ // 保留表达式
267
+ } else {
268
+ // 修复现有的参数值
269
+ const castedValue = toArgumentValue(currentValue, param) as V | null;
270
+ if (castedValue != null && castedValue !== currentValue) {
271
+ args[key] = castedValue;
272
+ }
273
+ }
274
+ }
275
+ keys.delete(key);
276
+ }
277
+ }
278
+
279
+ // 删除多余的参数
280
+ for (const key of keys) {
281
+ // 保留特殊参数和私有字段
282
+ if (typeof key != 'string') continue;
283
+ const k0 = key[0];
284
+ if (k0 === '_' || k0 === '$' || k0 === '@' || k0 === 'ɵ') continue;
285
+ delete args[key];
286
+ }
287
+ return args;
288
+ }
@@ -12,7 +12,7 @@ export type Variable<T extends ArgumentValue = ArgumentValue> = {
12
12
  /** 检查是否为 `Variable` */
13
13
  export function isVariable(obj: unknown): obj is Variable {
14
14
  if (obj == null || typeof obj != 'object') return false;
15
- if ('condition' in obj || 'description' in obj) return false;
15
+ if ('condition' in obj || 'description' in obj || 'type' in obj) return false;
16
16
  const v = obj as Variable;
17
17
  return typeof v.key == 'string' && 'value' in v;
18
18
  }
@@ -4,31 +4,4 @@ export * from './definitions/parameter.js';
4
4
  export * from './definitions/parameter-group.js';
5
5
  export * from './definitions/parameter-decoration.js';
6
6
  export * from './definitions/variable.js';
7
-
8
- export {
9
- isVmAny,
10
- isVmArray,
11
- isVmConst,
12
- isVmContext,
13
- isVmExtern,
14
- isVmFunction,
15
- isVmImmutable,
16
- isVmModule,
17
- isVmPrimitive,
18
- isVmRecord,
19
- isVmValue,
20
- } from '@mirascript/mirascript';
21
- export type {
22
- VmExtern,
23
- VmFunction,
24
- VmModule,
25
- VmAny,
26
- VmArray,
27
- VmConst,
28
- VmContext,
29
- VmImmutable,
30
- VmPrimitive,
31
- VmRecord,
32
- VmUninitialized,
33
- VmValue,
34
- } from '@mirascript/mirascript';
7
+ export * from './definitions/utils.js';
package/src/eval.ts CHANGED
@@ -1,64 +1,55 @@
1
1
  import type { VmValue } from '@mirascript/mirascript';
2
- import type { Context } from './context.js';
3
2
  import type { EvalExpressionCache, ExpressionCache } from './parser.js';
4
3
  import { Scope, unwrap } from './scope.js';
5
- import { TypeInfo } from './type.js';
6
-
7
- export const DEFAULTS = Symbol('defaults');
4
+ import type { Evaluator } from './main.js';
8
5
 
9
6
  /**
10
7
  * 表达式求值
11
8
  */
12
9
  export function evaluateEval<T extends VmValue>(
13
- context: Context,
10
+ evaluator: Evaluator | null,
14
11
  expression: EvalExpressionCache,
15
12
  scope: Scope,
16
- ): T | typeof DEFAULTS {
13
+ ): T | null {
17
14
  try {
18
15
  const result = expression.func(scope.proxy);
19
- if (result == null) return DEFAULTS;
16
+ if (result == null) return null;
20
17
  return unwrap(result as T);
21
18
  } catch (error: unknown) {
22
19
  if (scope.throws) throw error;
23
- context.logger.warn(
20
+ (evaluator?.logger ?? console).warn(
24
21
  `Failed to execute expression "${expression.source}": ${(error as Error).message || String(error)}.`,
25
22
  {
26
23
  error,
27
24
  scope,
28
25
  },
29
26
  );
30
- return DEFAULTS;
27
+ return null;
31
28
  }
32
29
  }
33
30
 
34
31
  /**
35
32
  * 表达式求值
36
33
  */
37
- export function evaluate<T extends VmValue, D = T>(
38
- context: Context,
34
+ export function evaluate<T extends VmValue>(
35
+ evaluator: Evaluator | null,
39
36
  expression: ExpressionCache,
40
- returnType: TypeInfo | null,
41
- scope: Scope | undefined,
42
- defaults: D,
43
- ): D | T {
44
- let ret: T | typeof DEFAULTS;
37
+ scope: Scope,
38
+ ): T | null {
39
+ let ret: T | null = null;
45
40
  if (expression.func != null) {
46
41
  scope ??= new Scope();
47
- const reset = scope.reset(context);
42
+ const reset = scope.reset(evaluator);
48
43
  try {
49
- ret = evaluateEval<T>(context, expression, scope);
44
+ ret = evaluateEval<T>(evaluator, expression, scope);
50
45
  } finally {
51
46
  reset();
52
47
  }
53
48
  } else if (expression.error != null) {
54
- if (scope?.throws ?? true) {
49
+ if (scope.throws) {
55
50
  throw expression.error;
56
51
  }
57
- ret = DEFAULTS;
58
- } else {
59
- ret = DEFAULTS;
60
52
  }
61
- if (ret === DEFAULTS) return defaults;
62
- if (!returnType) return ret;
63
- return TypeInfo.to(ret, returnType) as T;
53
+ if (ret == null) return null;
54
+ return ret;
64
55
  }
package/src/expression.ts CHANGED
@@ -2,6 +2,7 @@ import type { VmValue } from '@mirascript/mirascript';
2
2
  import type { Tagged } from 'type-fest';
3
3
  import type { LegacyType, TsTypeOf, TypeInfo } from './type.js';
4
4
  import type { Scope } from './scope.js';
5
+ import type { Evaluator } from './main.js';
5
6
 
6
7
  /** 表达式的标记属性 */
7
8
  export const ExpressionTag = 'ɵexp' as const;
@@ -18,10 +19,7 @@ export type Expression<T extends VmValue = VmValue> = {
18
19
  /**
19
20
  * 编译后的表达式
20
21
  */
21
- export interface CompiledExpression<T extends VmValue = VmValue> extends Expression<T> {
22
- (scope: Scope, defaults: T): T;
23
- (scope?: Scope): T | undefined;
24
- }
22
+ export type CompiledExpression<T extends VmValue = VmValue> = Expression<T> & ExpressionFunction<T>;
25
23
 
26
24
  /** 表示一个表达式内容 */
27
25
  export type ExpressionSource<T extends VmValue = VmValue> = Tagged<string, 'ExpressionResult', T>;
@@ -41,16 +39,16 @@ export function Expression<T extends VmValue = VmValue>(
41
39
  source: string | Expression<T>,
42
40
  returnType: TypeInfo | LegacyType | '' = '',
43
41
  ): Expression<T> {
44
- if (!source) throw new Error(`Invalid expression ${String(source)}`);
42
+ if (source == null) throw new TypeError(`Invalid expression ${String(source)}`);
45
43
  const s = isExpression(source) ? source.source : source;
46
- if (!s || typeof s != 'string') throw new Error(`Invalid expression ${String(s)}`);
47
- return { [ExpressionTag]: returnType, source: s as ExpressionSource<T> };
44
+ if (typeof s != 'string') throw new TypeError(`Invalid expression ${String(s)}`);
45
+ return { [ExpressionTag]: returnType || '', source: s as ExpressionSource<T> };
48
46
  }
49
47
 
50
48
  /** 检查是否为表达式 */
51
49
  export function isExpression<T extends VmValue>(value: unknown): value is Expression<T> | CompiledExpression<T> {
52
50
  if (value == null || (typeof value != 'object' && typeof value != 'function')) return false;
53
- return ExpressionTag in value;
51
+ return typeof (value as Expression<T>)[ExpressionTag] == 'string';
54
52
  }
55
53
 
56
54
  /** 表示一个表达式求值的结果 */
@@ -60,3 +58,13 @@ export type ExpressionResult<T> = T extends Expression<infer R> ? R : T;
60
58
  export type ExpressionResultObject<T> = {
61
59
  [K in keyof T]: ExpressionResult<T[K]>;
62
60
  };
61
+
62
+ /**
63
+ * 表示一个具有类似表达式功能的 JS 函数
64
+ */
65
+ export type ExpressionFunction<out T> = (
66
+ /** 当前作用域 */
67
+ scope: Scope,
68
+ /** 当前求值器 */
69
+ evaluator: Evaluator,
70
+ ) => T | null;
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
- export type { Options, Context } from './context.js';
1
+ export type { Options, Logger } from './interface.js';
2
2
  export { Scope } from './scope.js';
3
3
  export { Evaluator } from './main.js';
4
+ export type { GlobalReferenceChain } from './analyze.js';
4
5
  export * from './expression.js';
5
6
  export * from './type.js';
7
+ export * from './re-exports.js';
@@ -0,0 +1,35 @@
1
+ /** 日志 */
2
+ export interface Logger {
3
+ /**
4
+ * error
5
+ */
6
+ error(...args: unknown[]): void;
7
+ /**
8
+ * warning
9
+ */
10
+ warn(...args: unknown[]): void;
11
+ /**
12
+ * info
13
+ */
14
+ info(...args: unknown[]): void;
15
+ /**
16
+ * debug
17
+ */
18
+ debug(...args: unknown[]): void;
19
+ }
20
+
21
+ /** 选项 */
22
+ export interface Options {
23
+ /** 日志 */
24
+ logger: Logger;
25
+ /** 表达式缓存 */
26
+ expressionCacheSize: number;
27
+ /** 模板缓存 */
28
+ templateCacheSize: number;
29
+ }
30
+
31
+ export const defaultOptions: Options = {
32
+ logger: console,
33
+ expressionCacheSize: 50,
34
+ templateCacheSize: 50,
35
+ };