@cloudpss/expression 0.6.0-alpha.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 (165) hide show
  1. package/README.md +3 -0
  2. package/dist/analyze.d.ts +7 -0
  3. package/dist/analyze.d.ts.map +1 -0
  4. package/dist/analyze.js +38 -0
  5. package/dist/analyze.js.map +1 -0
  6. package/dist/context.d.ts +41 -0
  7. package/dist/context.d.ts.map +1 -0
  8. package/dist/context.js +18 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/definitions/argument.d.ts +33 -0
  11. package/dist/definitions/argument.d.ts.map +1 -0
  12. package/dist/definitions/argument.js +2 -0
  13. package/dist/definitions/argument.js.map +1 -0
  14. package/dist/definitions/constraint.d.ts +32 -0
  15. package/dist/definitions/constraint.d.ts.map +1 -0
  16. package/dist/definitions/constraint.js +20 -0
  17. package/dist/definitions/constraint.js.map +1 -0
  18. package/dist/definitions/parameter-decoration.d.ts +27 -0
  19. package/dist/definitions/parameter-decoration.d.ts.map +1 -0
  20. package/dist/definitions/parameter-decoration.js +17 -0
  21. package/dist/definitions/parameter-decoration.js.map +1 -0
  22. package/dist/definitions/parameter-group.d.ts +38 -0
  23. package/dist/definitions/parameter-group.d.ts.map +1 -0
  24. package/dist/definitions/parameter-group.js +31 -0
  25. package/dist/definitions/parameter-group.js.map +1 -0
  26. package/dist/definitions/parameter.d.ts +214 -0
  27. package/dist/definitions/parameter.d.ts.map +1 -0
  28. package/dist/definitions/parameter.js +44 -0
  29. package/dist/definitions/parameter.js.map +1 -0
  30. package/dist/definitions/variable.d.ts +14 -0
  31. package/dist/definitions/variable.d.ts.map +1 -0
  32. package/dist/definitions/variable.js +14 -0
  33. package/dist/definitions/variable.js.map +1 -0
  34. package/dist/definitions.d.ts +7 -0
  35. package/dist/definitions.d.ts.map +1 -0
  36. package/dist/definitions.js +7 -0
  37. package/dist/definitions.js.map +1 -0
  38. package/dist/eval.d.ts +15 -0
  39. package/dist/eval.d.ts.map +1 -0
  40. package/dist/eval.js +54 -0
  41. package/dist/eval.js.map +1 -0
  42. package/dist/expression.d.ts +37 -0
  43. package/dist/expression.d.ts.map +1 -0
  44. package/dist/expression.js +18 -0
  45. package/dist/expression.js.map +1 -0
  46. package/dist/index.d.ts +6 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +5 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/main.d.ts +49 -0
  51. package/dist/main.d.ts.map +1 -0
  52. package/dist/main.js +238 -0
  53. package/dist/main.js.map +1 -0
  54. package/dist/migrate.d.ts +5 -0
  55. package/dist/migrate.d.ts.map +1 -0
  56. package/dist/migrate.js +23 -0
  57. package/dist/migrate.js.map +1 -0
  58. package/dist/migrator/access.d.ts +6 -0
  59. package/dist/migrator/access.d.ts.map +1 -0
  60. package/dist/migrator/access.js +176 -0
  61. package/dist/migrator/access.js.map +1 -0
  62. package/dist/migrator/call.d.ts +6 -0
  63. package/dist/migrator/call.d.ts.map +1 -0
  64. package/dist/migrator/call.js +405 -0
  65. package/dist/migrator/call.js.map +1 -0
  66. package/dist/migrator/concat.d.ts +6 -0
  67. package/dist/migrator/concat.d.ts.map +1 -0
  68. package/dist/migrator/concat.js +32 -0
  69. package/dist/migrator/concat.js.map +1 -0
  70. package/dist/migrator/condition.d.ts +6 -0
  71. package/dist/migrator/condition.d.ts.map +1 -0
  72. package/dist/migrator/condition.js +110 -0
  73. package/dist/migrator/condition.js.map +1 -0
  74. package/dist/migrator/interface.d.ts +24 -0
  75. package/dist/migrator/interface.d.ts.map +1 -0
  76. package/dist/migrator/interface.js +8 -0
  77. package/dist/migrator/interface.js.map +1 -0
  78. package/dist/migrator/node.d.ts +12 -0
  79. package/dist/migrator/node.d.ts.map +1 -0
  80. package/dist/migrator/node.js +119 -0
  81. package/dist/migrator/node.js.map +1 -0
  82. package/dist/migrator/operator.d.ts +6 -0
  83. package/dist/migrator/operator.d.ts.map +1 -0
  84. package/dist/migrator/operator.js +299 -0
  85. package/dist/migrator/operator.js.map +1 -0
  86. package/dist/migrator/parser.d.ts +3 -0
  87. package/dist/migrator/parser.d.ts.map +1 -0
  88. package/dist/migrator/parser.js +76 -0
  89. package/dist/migrator/parser.js.map +1 -0
  90. package/dist/migrator/special.d.ts +4 -0
  91. package/dist/migrator/special.d.ts.map +1 -0
  92. package/dist/migrator/special.js +26 -0
  93. package/dist/migrator/special.js.map +1 -0
  94. package/dist/migrator/state.d.ts +64 -0
  95. package/dist/migrator/state.d.ts.map +1 -0
  96. package/dist/migrator/state.js +251 -0
  97. package/dist/migrator/state.js.map +1 -0
  98. package/dist/migrator/symbol.d.ts +6 -0
  99. package/dist/migrator/symbol.d.ts.map +1 -0
  100. package/dist/migrator/symbol.js +50 -0
  101. package/dist/migrator/symbol.js.map +1 -0
  102. package/dist/migrator/to-type.d.ts +10 -0
  103. package/dist/migrator/to-type.d.ts.map +1 -0
  104. package/dist/migrator/to-type.js +89 -0
  105. package/dist/migrator/to-type.js.map +1 -0
  106. package/dist/migrator/utils.d.ts +14 -0
  107. package/dist/migrator/utils.d.ts.map +1 -0
  108. package/dist/migrator/utils.js +64 -0
  109. package/dist/migrator/utils.js.map +1 -0
  110. package/dist/parser.d.ts +41 -0
  111. package/dist/parser.d.ts.map +1 -0
  112. package/dist/parser.js +36 -0
  113. package/dist/parser.js.map +1 -0
  114. package/dist/scope.d.ts +43 -0
  115. package/dist/scope.d.ts.map +1 -0
  116. package/dist/scope.js +204 -0
  117. package/dist/scope.js.map +1 -0
  118. package/dist/type.d.ts +21 -0
  119. package/dist/type.d.ts.map +1 -0
  120. package/dist/type.js +50 -0
  121. package/dist/type.js.map +1 -0
  122. package/jest.config.js +3 -0
  123. package/package.json +26 -0
  124. package/src/analyze.ts +44 -0
  125. package/src/context.ts +54 -0
  126. package/src/definitions/argument.ts +40 -0
  127. package/src/definitions/constraint.ts +49 -0
  128. package/src/definitions/parameter-decoration.ts +42 -0
  129. package/src/definitions/parameter-group.ts +62 -0
  130. package/src/definitions/parameter.ts +276 -0
  131. package/src/definitions/variable.ts +23 -0
  132. package/src/definitions.ts +6 -0
  133. package/src/eval.ts +64 -0
  134. package/src/expression.ts +62 -0
  135. package/src/index.ts +5 -0
  136. package/src/main.ts +295 -0
  137. package/src/migrate.ts +25 -0
  138. package/src/migrator/access.ts +201 -0
  139. package/src/migrator/call.ts +397 -0
  140. package/src/migrator/concat.ts +28 -0
  141. package/src/migrator/condition.ts +120 -0
  142. package/src/migrator/interface.ts +30 -0
  143. package/src/migrator/node.ts +131 -0
  144. package/src/migrator/operator.ts +339 -0
  145. package/src/migrator/parser.ts +106 -0
  146. package/src/migrator/special.ts +30 -0
  147. package/src/migrator/state.ts +262 -0
  148. package/src/migrator/symbol.ts +53 -0
  149. package/src/migrator/to-type.ts +93 -0
  150. package/src/migrator/utils.ts +67 -0
  151. package/src/parser.ts +69 -0
  152. package/src/scope.ts +208 -0
  153. package/src/type.ts +66 -0
  154. package/tests/analyze.ts +15 -0
  155. package/tests/condition.ts +49 -0
  156. package/tests/definition.ts +42 -0
  157. package/tests/eval-complex.ts +150 -0
  158. package/tests/eval.ts +42 -0
  159. package/tests/functions.ts +22 -0
  160. package/tests/import.ts +31 -0
  161. package/tests/scope.ts +33 -0
  162. package/tests/setup.ts +41 -0
  163. package/tests/tsconfig.json +3 -0
  164. package/tests/unwrap.ts +46 -0
  165. package/tsconfig.json +3 -0
package/src/analyze.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { Context } from './context.js';
2
+ import type { ExpressionCache } from './parser.js';
3
+
4
+ /**
5
+ * 分析依赖
6
+ */
7
+ export function analyze(context: Context, expression: ExpressionCache): string[][] {
8
+ // TODO:
9
+ return [];
10
+ // if (expression.ast == null) return [];
11
+
12
+ // const deps: string[][] = [];
13
+
14
+ // const analyzeNode = (node: MathNode): string[] | undefined => {
15
+ // if (isSymbolNode(node) && node.name) {
16
+ // return [node.name];
17
+ // }
18
+ // if (isAccessorNode(node)) {
19
+ // const { index } = node;
20
+ // if (index.dimensions.length !== 1 || !isConstantNode(index.dimensions[0])) {
21
+ // return undefined;
22
+ // }
23
+ // const indexValue = index.dimensions[0].value;
24
+ // // 暂时不考虑,两边索引 base 不一致
25
+ // if (typeof indexValue != 'string' /*&& typeof indexValue != 'number'*/) {
26
+ // return undefined;
27
+ // }
28
+ // const deps = analyzeNode(node.object);
29
+ // if (!deps) return undefined;
30
+
31
+ // return [...deps, indexValue];
32
+ // }
33
+ // return undefined;
34
+ // };
35
+
36
+ // const analyzeAndPush = (node: MathNode): void => {
37
+ // const dep = analyzeNode(node);
38
+ // if (dep) deps.push(dep);
39
+ // // eslint-disable-next-line unicorn/no-array-for-each
40
+ // else node.forEach(analyzeAndPush);
41
+ // };
42
+ // analyzeAndPush(expression.ast);
43
+ // return deps;
44
+ }
package/src/context.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ import type { ExpressionCache } from './parser.js';
3
+ import type { VmValue } from '@mirascript/mirascript';
4
+
5
+ /** 日志 */
6
+ export interface Logger {
7
+ /**
8
+ * error
9
+ */
10
+ error(...args: unknown[]): void;
11
+ /**
12
+ * warning
13
+ */
14
+ warn(...args: unknown[]): void;
15
+ /**
16
+ * info
17
+ */
18
+ info(...args: unknown[]): void;
19
+ /**
20
+ * debug
21
+ */
22
+ debug(...args: unknown[]): void;
23
+ }
24
+
25
+ /** 选项 */
26
+ export interface Options {
27
+ /** 日志 */
28
+ logger: Logger;
29
+ /** 表达式缓存 */
30
+ expressionCacheSize: number;
31
+ }
32
+
33
+ const defaultOptions: Options = {
34
+ logger: console,
35
+ expressionCacheSize: 50,
36
+ };
37
+
38
+ /** 执行上下文 */
39
+ export class Context {
40
+ constructor(options?: Partial<Options>) {
41
+ this.options = { ...options, ...defaultOptions };
42
+ this.expressionCache = new LRUCache<string, ExpressionCache>({ max: this.options.expressionCacheSize });
43
+ }
44
+ readonly imported = new Map<string, VmValue>();
45
+ /** Logger */
46
+ get logger(): Logger {
47
+ return this.options.logger ?? console;
48
+ }
49
+ /** 选项 */
50
+ readonly options: Options;
51
+
52
+ /** 表达式缓存 */
53
+ readonly expressionCache;
54
+ }
@@ -0,0 +1,40 @@
1
+ import type { VmConst, VmValue } from '@mirascript/mirascript';
2
+ import type { ExpressionOrValue, ExpressionSource } from '../expression.js';
3
+ import type { Scope } from '../scope.js';
4
+ import type { Evaluator } from '../main.js';
5
+
6
+ /**
7
+ * 表示一个具有类似表达式功能的 JS 函数,与某个 {@link ArgumentValue} 相关联,在其之后求值
8
+ */
9
+ export type ExpressionFunction<R, V extends VmValue = ArgumentValue> = (
10
+ /** 相关联的参数值 */
11
+ value: V,
12
+ /** 当前作用域 */
13
+ scope: Scope,
14
+ /** 当前求值器 */
15
+ evaluator: Evaluator,
16
+ ) => R;
17
+
18
+ /**
19
+ * 表示一个具有类似表达式功能的 JS 函数,与某个 {@link ArgumentValue} 相关联,在其之前求值
20
+ */
21
+ export type ParameterFunction<R> = (
22
+ /** 当前作用域 */
23
+ scope: Scope,
24
+ /** 当前求值器 */
25
+ evaluator: Evaluator,
26
+ ) => R;
27
+
28
+ /** 条件表达式 */
29
+ export type ConditionExpression = boolean | ExpressionSource<boolean>;
30
+
31
+ /** 条件函数 */
32
+ export type ConditionFunction<V extends VmValue = ArgumentValue> = ExpressionFunction<boolean, V>;
33
+
34
+ /** 参数值 */
35
+ export type ArgumentValue = NonNullable<VmConst>;
36
+
37
+ /** 未求值的参数表 */
38
+ export type ArgumentMap<T extends Record<string, ArgumentValue> = Record<string, ArgumentValue>> = {
39
+ [K in keyof T & string]?: ExpressionOrValue<NonNullable<T[K]>>;
40
+ };
@@ -0,0 +1,49 @@
1
+ import type { VmRecord, VmValue } from '@mirascript/mirascript';
2
+ import type { ArgumentValue, ConditionExpression, ConditionFunction, ExpressionFunction } from './argument.js';
3
+ import type { ExpressionSource } from '../expression.js';
4
+
5
+ export const ConstraintLevel = Object.freeze(['info', 'warning', 'error'] as const);
6
+ /** 约束条件的级别 */
7
+ export type ConstraintLevel = (typeof ConstraintLevel)[number];
8
+
9
+ /** 表示参数值需要满足的约束条件 */
10
+ export interface Constraint<V extends VmValue = ArgumentValue> {
11
+ /**
12
+ * 指定约束条件检查顺序,顺序为 `'pre'` `(builtin)` `undefined` `'post'`
13
+ */
14
+ order?: 'pre' | 'post';
15
+ /**
16
+ * 不满足条件的提示
17
+ */
18
+ message: string;
19
+ /**
20
+ * 不满足条件的提示级别,仅 `'error'` 会导致参数无效,且后续约束不再检查
21
+ */
22
+ level?: ConstraintLevel;
23
+ /**
24
+ * 不满足条件的提示附加参数
25
+ */
26
+ messageData?: ExpressionSource<VmRecord> | VmRecord | ExpressionFunction<VmRecord, V>;
27
+ /**
28
+ * 约束条件
29
+ */
30
+ condition: ConditionExpression | ConditionFunction<V>;
31
+ }
32
+
33
+ /** 检查是否为 Constraint */
34
+ export function isConstraint(value: unknown): value is Constraint {
35
+ if (value == null || typeof value !== 'object') return false;
36
+ if ('key' in value || 'description' in value) return false;
37
+
38
+ const v = value as Constraint;
39
+ if (typeof v.message != 'string') return false;
40
+ if (typeof v.condition != 'function' && typeof v.condition != 'string') return false;
41
+ if (
42
+ v.messageData != null &&
43
+ typeof v.messageData != 'string' &&
44
+ typeof v.messageData != 'object' &&
45
+ typeof v.messageData != 'function'
46
+ )
47
+ return false;
48
+ return true;
49
+ }
@@ -0,0 +1,42 @@
1
+ import type { ConditionExpression } from './argument.js';
2
+
3
+ /** 参数界面装饰 */
4
+ export interface ParameterDecorationBase<T extends string> {
5
+ /** 参数装饰类型 */
6
+ type: T;
7
+ /** 布局时占用的位置 */
8
+ span?: number;
9
+ /** 装饰可见的条件表达式 */
10
+ condition?: ConditionExpression;
11
+ }
12
+
13
+ /** 参数标签装饰 */
14
+ export interface ParameterLabelDecoration extends ParameterDecorationBase<'label'> {
15
+ /** 标签文本 */
16
+ label: string;
17
+ }
18
+
19
+ /** 参数界面装饰映射 */
20
+ export interface ParameterDecorationMap {
21
+ /** 参数标签装饰 */
22
+ label: ParameterLabelDecoration;
23
+ }
24
+
25
+ /** 参数界面装饰 */
26
+ export type ParameterDecoration = ParameterDecorationMap[keyof ParameterDecorationMap];
27
+
28
+ /** 是否为参数界面装饰 */
29
+ export function isParameterDecoration(value: unknown): value is ParameterDecoration {
30
+ if (value == null || typeof value != 'object') return false;
31
+ const v = value as ParameterDecoration;
32
+ if ('key' in v || 'name' in v || 'description' in v) return false;
33
+ return typeof v.type == 'string' && v.type.length > 0;
34
+ }
35
+
36
+ /** 创建参数界面装饰 */
37
+ export function ParameterDecoration(): ParameterLabelDecoration {
38
+ return {
39
+ type: 'label',
40
+ label: '',
41
+ };
42
+ }
@@ -0,0 +1,62 @@
1
+ import type { ConditionExpression } from './argument.js';
2
+ import type { ParameterDecoration } from './parameter-decoration.js';
3
+ import type { Parameter } from './parameter.js';
4
+
5
+ /** 参数分组的公共属性 */
6
+ interface ParameterGroupBase {
7
+ /** 分组可见的条件表达式 */
8
+ condition?: ConditionExpression;
9
+ /** 布局时占用的位置 */
10
+ span?: number;
11
+ }
12
+
13
+ /** 装饰分组 */
14
+ export interface ParameterDecorationGroup extends ParameterGroupBase {
15
+ /** 装饰列表 */
16
+ items: ParameterDecoration[];
17
+ }
18
+
19
+ /** 创建装饰分组 */
20
+ export function ParameterDecorationGroup(): ParameterDecorationGroup {
21
+ return {
22
+ items: [],
23
+ };
24
+ }
25
+
26
+ /** 判断是否为装饰分组 */
27
+ export function isParameterDecorationGroup(value: unknown): value is ParameterDecorationGroup {
28
+ if (value == null || typeof value !== 'object') return false;
29
+ const v = value as ParameterDecorationGroup;
30
+ if ('key' in v || 'name' in v || 'description' in v) return false;
31
+ return Array.isArray(v.items);
32
+ }
33
+
34
+ /** 参数分组 */
35
+ export interface ParameterGroup extends ParameterGroupBase {
36
+ /** 名称 */
37
+ name: string;
38
+ /** 详细描述 */
39
+ description: string;
40
+ /** 包含的参数列表 */
41
+ items: Parameter[];
42
+ /** 装饰 */
43
+ before?: ParameterDecorationGroup;
44
+ /** 装饰 */
45
+ after?: ParameterDecorationGroup;
46
+ }
47
+
48
+ /** 创建参数分组 */
49
+ export function ParameterGroup(): ParameterGroup {
50
+ return {
51
+ name: '',
52
+ description: '',
53
+ items: [],
54
+ };
55
+ }
56
+
57
+ /** 判断是否为参数分组 */
58
+ export function isParameterGroup(item: unknown): item is ParameterGroup {
59
+ if (item == null || typeof item !== 'object') return false;
60
+ const group = item as ParameterGroup;
61
+ return typeof group.name === 'string' && typeof group.description === 'string' && Array.isArray(group.items);
62
+ }
@@ -0,0 +1,276 @@
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';
4
+ import type { Constraint } from './constraint.js';
5
+ import type { ParameterDecoration } from './parameter-decoration.js';
6
+ import type { ParameterGroup } from './parameter-group.js';
7
+
8
+ export type { ArgumentMap };
9
+ /** 参数定义 */
10
+ export interface ParameterBase<T extends string, V extends ArgumentValue> {
11
+ /** 参数类型 */
12
+ type: T;
13
+ /** 实现阶段用于索引的键值,即 {@link ArgumentMap} 的 key */
14
+ key: string;
15
+ /** 名称 */
16
+ name: string;
17
+ /** 详细描述 */
18
+ description: string;
19
+ /** 参数可用的条件表达式 */
20
+ condition?: ConditionExpression;
21
+ /** 参数初始值(默认值) */
22
+ value: ExpressionOrValue<V>;
23
+ /** 需要满足的其他约束 */
24
+ constraints?: Array<Constraint<V>>;
25
+ /** 布局时占用的位置 */
26
+ span?: number;
27
+ /** 装饰 */
28
+ before?: ParameterDecoration;
29
+ /** 装饰 */
30
+ after?: ParameterDecoration;
31
+ }
32
+
33
+ export const NumericParameterInterval = Object.freeze(['open', 'left-open', 'right-open', 'closed'] as const);
34
+ /**
35
+ * 区间类型
36
+ */
37
+ export type NumericParameterInterval = (typeof NumericParameterInterval)[number];
38
+
39
+ /**
40
+ * 数值类型参数定义
41
+ */
42
+ export interface NumericParameter<T extends string> extends ParameterBase<T, number> {
43
+ /** 允许的最小值 */
44
+ minimum?: number;
45
+ /** 允许的最大值 */
46
+ maximum?: number;
47
+ /** 区间选项 */
48
+ interval?: NumericParameterInterval;
49
+ /** 数值单位 */
50
+ unit?: string;
51
+ }
52
+
53
+ /**
54
+ * 实数类型参数定义
55
+ */
56
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
57
+ export interface RealParameter extends NumericParameter<'real'> {}
58
+
59
+ /**
60
+ * 整数类型参数定义
61
+ */
62
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
63
+ export interface IntegerParameter extends NumericParameter<'integer'> {}
64
+
65
+ /** 可用于选项的值 */
66
+ export type ChoiceArgumentValue = NonNullable<VmPrimitive>;
67
+
68
+ /** 表示一个选项 Key,当其值为数字时,默认值为其在数组中的索引 */
69
+ type ChoiceKey<V extends ChoiceArgumentValue> = V extends number ? number | null : V;
70
+
71
+ /** 表示一个选项 */
72
+ export type Choice<V extends ChoiceArgumentValue> = {
73
+ /** 选中该选项将存储的参数值 */
74
+ key: ChoiceKey<V>;
75
+ /** 选项名称 */
76
+ name: string;
77
+ /** 选项详细描述 */
78
+ description: string;
79
+ /** 选项可用的条件表达式 */
80
+ condition?: ConditionExpression;
81
+ };
82
+
83
+ /** 是否为选项 */
84
+ export function isChoice<V extends NonNullable<VmPrimitive>>(value: unknown): value is Choice<V> {
85
+ if (value == null || typeof value != 'object') return false;
86
+ const c = value as Choice<V>;
87
+ return typeof c.name === 'string' && typeof c.description == 'string' && !('items' in c) && !('value' in c);
88
+ }
89
+ /** 创建数值选项 */
90
+ export function Choice(key?: ChoiceKey<number>): Choice<number>;
91
+ /** 创建布尔选项 */
92
+ export function Choice(key: ChoiceKey<boolean>): Choice<boolean>;
93
+ /** 创建字符串选项 */
94
+ export function Choice(key: ChoiceKey<string>): Choice<string>;
95
+ /** 创建选项 */
96
+ export function Choice<V extends NonNullable<VmPrimitive> = number>(key: ChoiceKey<V>): Choice<V>;
97
+ /** 创建选项 */
98
+ export function Choice<V extends NonNullable<VmPrimitive> = number>(key: ChoiceKey<V>): Choice<V> {
99
+ return {
100
+ key: key ?? (null as never),
101
+ name: '',
102
+ description: '',
103
+ };
104
+ }
105
+
106
+ /**
107
+ * 表示值为选择项的参数定义
108
+ */
109
+ export interface ChoiceParameter<V extends ChoiceArgumentValue> extends ParameterBase<'choice', V> {
110
+ /**
111
+ * 可选取值及其对应的说明文本
112
+ */
113
+ choices: ExpressionOrValue<Array<Choice<V>>> | ParameterFunction<Array<Choice<V>>>;
114
+ }
115
+ /**
116
+ * 表示值为多选选择项的参数定义
117
+ */
118
+ export interface MultiSelectParameter<V extends readonly ChoiceArgumentValue[]>
119
+ extends ParameterBase<'multiSelect', V> {
120
+ /**
121
+ * 可选取值及其对应的说明文本
122
+ */
123
+ choices: ExpressionOrValue<Array<Choice<V[number]>>> | ParameterFunction<Array<Choice<V[number]>>>;
124
+ }
125
+
126
+ /**
127
+ * 表示值为布尔量的参数定义
128
+ */
129
+ export interface LogicalParameter<V extends ChoiceArgumentValue> extends ParameterBase<'logical', V> {
130
+ /**
131
+ * 可选取值及其对应的说明文本
132
+ */
133
+ choices?:
134
+ | ExpressionOrValue<[false: Choice<V>, true: Choice<V>]>
135
+ | ParameterFunction<[false: Choice<V>, true: Choice<V>]>;
136
+ }
137
+
138
+ /**
139
+ * 表示值为表格的参数定义
140
+ */
141
+ export interface TableParameter<V extends readonly ArgumentValue[]> extends ParameterBase<'table', V> {
142
+ /** 最小行数 */
143
+ minRowCount?: number;
144
+ /** 最大行数 */
145
+ maxRowCount?: number;
146
+ /**
147
+ * 各列的定义
148
+ *
149
+ * `key` 默认为列定义在 {@link columns} 中的索引
150
+ *
151
+ * 当仅包含一列时,表格参数的值为该列定义的数组;
152
+ *
153
+ * 否则,表格参数的值为记录类型数组,每个记录包含各列定义的键值
154
+ */
155
+ columns: Parameter[] | ParameterFunction<Parameter[]>;
156
+ }
157
+
158
+ /**
159
+ * 表示值为简单记录类型的定义
160
+ *
161
+ * 简单记录类型包含一组字段,其编辑界面按字段顺序线性排列,可以沿行或列排列。
162
+ *
163
+ * 字段中可包含占位符,占位符在主轴上尺寸可自定义。
164
+ */
165
+ export interface RecordParameter<V extends VmRecord> extends ParameterBase<'record', V> {
166
+ /** 主轴的优先方向 */
167
+ direction?: 'row' | 'column';
168
+ /** 包含的元素 */
169
+ items: Parameter[];
170
+ }
171
+
172
+ /**
173
+ * 表示值为包含分组的复杂记录的定义
174
+ *
175
+ * 每个分组包含一组字段,与简单记录类似。在编辑界面,分组名和内容沿正交轴线型排列。当所有分组都没有名字时,分组名会被隐藏。
176
+ *
177
+ * 各分组沿主轴排列时占据位置由 span 定义。
178
+ */
179
+ export interface GroupedParameter<V extends VmRecord> extends ParameterBase<'grouped', V> {
180
+ /** 主轴的优先方向 */
181
+ direction?: 'row' | 'column';
182
+ /** 包含的元素 */
183
+ items: ParameterGroup[];
184
+ }
185
+
186
+ /** 表示值为文本的参数定义 */
187
+ export interface TextParameter extends ParameterBase<'text', string> {
188
+ /** 验证有效性的正则表达式 */
189
+ pattern?: string;
190
+ /** 验证未通过的提示信息 */
191
+ patternErrorMessage?: string;
192
+ /** 最小长度 */
193
+ minLength?: number;
194
+ /** 最大长度 */
195
+ maxLength?: number;
196
+ }
197
+
198
+ /** 表示值为代码的参数定义 */
199
+ export interface CodeParameter extends ParameterBase<'code', string> {
200
+ /** 代码的语言 */
201
+ language?: string;
202
+ }
203
+
204
+ /**
205
+ * 文件类型说明符
206
+ * @example ".png" "image/*"
207
+ */
208
+ export type FileTypeDescriptor = `.${string}` | `${string}/${string}`;
209
+
210
+ /** 接受的文件内容格式 */
211
+ export type FileUrlCategory =
212
+ // data url
213
+ | 'embed'
214
+ // cloudpss-storage url
215
+ | 'storage'
216
+ // http/https url
217
+ | 'link';
218
+
219
+ /** 表示值为文件的参数定义 */
220
+ export interface FileParameter extends ParameterBase<'file', string> {
221
+ /** 支持的文件类型 */
222
+ accept?: FileTypeDescriptor | FileTypeDescriptor[];
223
+ /** 首选的文件处理方式 */
224
+ preference?: FileUrlCategory;
225
+ }
226
+
227
+ /**
228
+ * 已定义的参数类型
229
+ */
230
+ export type ParameterMap = {
231
+ real: RealParameter;
232
+ integer: IntegerParameter;
233
+ choice: ChoiceParameter<string> | ChoiceParameter<number> | ChoiceParameter<boolean>;
234
+ multiSelect: MultiSelectParameter<string[]> | MultiSelectParameter<number[]> | MultiSelectParameter<boolean[]>;
235
+ logical: LogicalParameter<string> | LogicalParameter<number> | LogicalParameter<boolean>;
236
+ table: TableParameter<ArgumentValue[]>;
237
+ text: TextParameter;
238
+ code: CodeParameter;
239
+ file: FileParameter;
240
+ record: RecordParameter<VmRecord>;
241
+ grouped: GroupedParameter<VmRecord>;
242
+ };
243
+ /**
244
+ * 已定义的参数类型
245
+ */
246
+ export type Parameter = ParameterMap[keyof ParameterMap];
247
+
248
+ /**
249
+ * 是否为参数定义
250
+ */
251
+ export function isParameter(value: unknown): value is Parameter {
252
+ if (value == null || typeof value != 'object') return false;
253
+ const v = value as Parameter;
254
+ return (
255
+ typeof v.key === 'string' &&
256
+ typeof v.type == 'string' &&
257
+ v.type.length > 0 &&
258
+ typeof v.name === 'string' &&
259
+ typeof v.description == 'string' &&
260
+ 'value' in v
261
+ );
262
+ }
263
+
264
+ /**
265
+ * 创建参数定义
266
+ */
267
+ export function Parameter(): RealParameter {
268
+ return {
269
+ key: '',
270
+ name: '',
271
+ description: '',
272
+ condition: '' as ConditionExpression,
273
+ type: 'real',
274
+ value: 0,
275
+ };
276
+ }
@@ -0,0 +1,23 @@
1
+ import type { ExpressionOrValue } from '../expression.js';
2
+ import type { ArgumentValue } from './argument.js';
3
+
4
+ /** 表示一个全局变量 */
5
+ export type Variable<T extends ArgumentValue = ArgumentValue> = {
6
+ /** 唯一 key */
7
+ key: string;
8
+ /** 变量值 */
9
+ value: ExpressionOrValue<T>;
10
+ };
11
+
12
+ /** 检查是否为 `Variable` */
13
+ export function isVariable(obj: unknown): obj is Variable {
14
+ if (obj == null || typeof obj != 'object') return false;
15
+ if ('condition' in obj || 'description' in obj) return false;
16
+ const v = obj as Variable;
17
+ return typeof v.key == 'string' && 'value' in v;
18
+ }
19
+
20
+ /** 创建空白 `Variable` */
21
+ export function Variable(): Variable {
22
+ return { key: '', value: '' };
23
+ }
@@ -0,0 +1,6 @@
1
+ export * from './definitions/argument.js';
2
+ export * from './definitions/constraint.js';
3
+ export * from './definitions/parameter.js';
4
+ export * from './definitions/parameter-group.js';
5
+ export * from './definitions/parameter-decoration.js';
6
+ export * from './definitions/variable.js';
package/src/eval.ts ADDED
@@ -0,0 +1,64 @@
1
+ import type { VmValue } from '@mirascript/mirascript';
2
+ import type { Context } from './context.js';
3
+ import type { EvalExpressionCache, ExpressionCache } from './parser.js';
4
+ import { Scope, unwrap } from './scope.js';
5
+ import { TypeInfo } from './type.js';
6
+
7
+ export const DEFAULTS = Symbol('defaults');
8
+
9
+ /**
10
+ * 表达式求值
11
+ */
12
+ export function evaluateEval<T extends VmValue>(
13
+ context: Context,
14
+ expression: EvalExpressionCache,
15
+ scope: Scope,
16
+ ): T | typeof DEFAULTS {
17
+ try {
18
+ const result = expression.func(scope.proxy);
19
+ if (result == null) return DEFAULTS;
20
+ return unwrap(result as T);
21
+ } catch (error: unknown) {
22
+ if (scope.throws) throw error;
23
+ context.logger.warn(
24
+ `Failed to execute expression "${expression.source}": ${(error as Error).message || String(error)}.`,
25
+ {
26
+ error,
27
+ scope,
28
+ },
29
+ );
30
+ return DEFAULTS;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * 表达式求值
36
+ */
37
+ export function evaluate<T extends VmValue, D = T>(
38
+ context: Context,
39
+ expression: ExpressionCache,
40
+ returnType: TypeInfo | null,
41
+ scope: Scope | undefined,
42
+ defaults: D,
43
+ ): D | T {
44
+ let ret: T | typeof DEFAULTS;
45
+ if (expression.func != null) {
46
+ scope ??= new Scope();
47
+ const reset = scope.reset(context);
48
+ try {
49
+ ret = evaluateEval<T>(context, expression, scope);
50
+ } finally {
51
+ reset();
52
+ }
53
+ } else if (expression.error != null) {
54
+ if (scope?.throws ?? true) {
55
+ throw expression.error;
56
+ }
57
+ ret = DEFAULTS;
58
+ } else {
59
+ ret = DEFAULTS;
60
+ }
61
+ if (ret === DEFAULTS) return defaults;
62
+ if (!returnType) return ret;
63
+ return TypeInfo.to(ret, returnType) as T;
64
+ }