@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
@@ -0,0 +1,27 @@
1
+ import type { FunctionAssignmentNode } from 'mathjs';
2
+ import type { Result } from './interface.js';
3
+ import { migrateAtomic } from './node.js';
4
+ import { type State, FunctionState } from './state.js';
5
+ import { migrateSymbolName } from './utils.js';
6
+
7
+ /** 转换函数定义语句 */
8
+ export function migrateFunctionAssignment(state: State, node: FunctionAssignmentNode): Result {
9
+ state.loose();
10
+ const { name, params, expr } = node;
11
+ const fState = new FunctionState(state, params);
12
+ let body = migrateAtomic(fState, expr);
13
+ if (!(body.code.startsWith('{') && body.code.endsWith('}'))) {
14
+ body = { ...body, code: `{ ${body.code} }` };
15
+ }
16
+ if (state.locals.has(name)) {
17
+ state.err(`重复定义函数: '${name}'`);
18
+ }
19
+ state.locals.set(name, {
20
+ type: 'function',
21
+ code: body.code,
22
+ });
23
+ return {
24
+ type: 'function',
25
+ code: `fn ${migrateSymbolName(name, true)}(${params.join(', ')}) ${body.code}`,
26
+ };
27
+ }
@@ -1,4 +1,4 @@
1
- import type { TypeName, VmConst } from '@mirascript/mirascript';
1
+ import type { TypeName, VmConst, VmValue } from '@mirascript/mirascript';
2
2
 
3
3
  /** 转换选项 */
4
4
  export interface Options {
@@ -12,6 +12,8 @@ export interface Result {
12
12
  readonly type?: TypeName;
13
13
  /** 为字面量 */
14
14
  readonly literal?: VmConst;
15
+ /** 为全局变量 */
16
+ readonly global?: VmValue;
15
17
  /** 转换后的代码 */
16
18
  readonly code: string;
17
19
  /** 转换后的代码 */
@@ -1,4 +1,4 @@
1
- import { serialize, serializePropName } from '@mirascript/mirascript/subtle';
1
+ import { serializeRecordKey } from '@mirascript/mirascript/subtle';
2
2
  import {
3
3
  type MathNode,
4
4
  isAccessorNode,
@@ -12,16 +12,21 @@ import {
12
12
  isRelationalNode,
13
13
  type OperatorNodeFn,
14
14
  isSymbolNode,
15
+ isFunctionAssignmentNode,
16
+ isRangeNode,
15
17
  } from 'mathjs';
16
18
  import type { State } from './state.js';
17
19
  import type { Options, Result } from './interface.js';
18
20
  import type { VmConst } from '@mirascript/mirascript';
19
- import { unsupportedNode } from './utils.js';
21
+ import { constantValue, unsupportedNode } from './utils.js';
20
22
  import { migrateAccess } from './access.js';
21
23
  import { migrateCall } from './call.js';
22
24
  import { migrateOperator } from './operator.js';
23
25
  import { migrateSymbol } from './symbol.js';
24
26
  import { migrateCondition } from './condition.js';
27
+ import { migrateFunctionAssignment } from './function.js';
28
+ import { serialize } from './serialize.js';
29
+ import { toNumber } from './to-type.js';
25
30
 
26
31
  /** 转换 AST */
27
32
  export function migrateAtomic(state: State, node: MathNode): Result {
@@ -79,7 +84,7 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
79
84
  } else {
80
85
  literals[key] = item.literal;
81
86
  }
82
- code.push(`${serializePropName(key)}: ${item.code}`);
87
+ code.push(`${serializeRecordKey(key)}: ${item.code}`);
83
88
  }
84
89
  return {
85
90
  type: 'record',
@@ -110,6 +115,26 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
110
115
  }
111
116
  if (isRelationalNode(node)) {
112
117
  const { conditionals, params } = node;
118
+ // 优化范围判断 a <= b <= c
119
+ if (
120
+ conditionals.length === 2 &&
121
+ params.length === 3 &&
122
+ conditionals[0] === 'smallerEq' &&
123
+ conditionals[1] === 'smallerEq'
124
+ ) {
125
+ // 模式匹配只支持常量
126
+ const l = constantValue(params[0]!);
127
+ const r = constantValue(params[2]!);
128
+ if (typeof l == 'number' && typeof r == 'number' && l < r) {
129
+ const v = toNumber(state, migrateNode(state, params[1]!, options));
130
+ let code = `${v.code} is ${l}..${r}`;
131
+ if (options.format !== 'no-paren') code = `(${code})`;
132
+ return {
133
+ type: 'boolean',
134
+ code,
135
+ };
136
+ }
137
+ }
113
138
  const exprs = [];
114
139
  for (let i = 0; i < conditionals.length; i++) {
115
140
  const fn = conditionals[i]! as OperatorNodeFn;
@@ -117,15 +142,40 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
117
142
  const r = params[i + 1]!;
118
143
  exprs.push(migrateOperator(state, { fn, args: [l, r] }, options));
119
144
  }
120
- let code = exprs.join(' && ');
145
+ let code = exprs.map((e) => e.code).join(' && ');
121
146
  if (options.format !== 'no-paren') code = `(${code})`;
122
147
  return {
123
148
  type: 'boolean',
124
149
  code,
125
150
  };
126
151
  }
127
- // if (isRangeNode(node)) {
152
+ if (isFunctionAssignmentNode(node)) {
153
+ const result = migrateFunctionAssignment(state, node);
154
+ state.helper(result.code);
155
+ return {
156
+ type: 'function',
157
+ code: node.name,
158
+ };
159
+ }
160
+ if (isRangeNode(node)) {
161
+ const { start, end, step } = node;
162
+ const stepValue = step == null ? 1 : constantValue(step);
163
+ // Math.js 的 : 与 MiraScript 的 .. 优先级相同,因此直接转换即可
164
+ const startNode = migrateExpr(state, start);
165
+ const endNode = migrateExpr(state, end);
166
+ if (stepValue === 1) {
167
+ return {
168
+ type: 'array',
169
+ code: `[${startNode.code}..${endNode.code}]`,
170
+ };
171
+ } else if (stepValue === -1) {
172
+ return {
173
+ type: 'array',
174
+ code: `[${endNode.code}..${startNode.code}]`,
175
+ };
176
+ }
177
+ return unsupportedNode(state, node);
178
+ }
128
179
  // if (isIndexNode(node)) {
129
- // if (isFunctionAssignmentNode(node)) {
130
180
  return unsupportedNode(state, node);
131
181
  }
@@ -8,61 +8,71 @@ import {
8
8
  isOperatorNode,
9
9
  isNode,
10
10
  } from 'mathjs';
11
- import { symbolName, constantValue, equalText, scalar } from './utils.js';
11
+ import { isVmArray, type TypeName, type VmAny } from '@mirascript/mirascript';
12
+ import { operations } from '@mirascript/mirascript/subtle';
13
+ import { symbolName, constantValue, equalText, scalar, globalFnName } from './utils.js';
12
14
  import type { State } from './state.js';
13
15
  import type { Options, Result } from './interface.js';
14
- import { migrateAtomic, migrateExpr } from './node.js';
16
+ import { migrateAtomic, migrateExpr, migrateParen } from './node.js';
15
17
  import { toBoolean, toNumber } from './to-type.js';
16
18
  import { migrateSymbol } from './symbol.js';
17
- import { operations, serialize } from '@mirascript/mirascript/subtle';
19
+ import { serialize } from './serialize.js';
18
20
 
19
21
  const BINARY_MATH_OPERATORS = {
20
22
  add: [
21
23
  ' + ',
22
- (l, r) => ({
24
+ (state, l, r) => ({
23
25
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
24
- code: `matrix.add(${l.code}, ${r.code})`,
26
+ code: `${globalFnName(state, 'matrix')}.add(${l.code}, ${r.code})`,
25
27
  }),
26
28
  ],
27
29
  subtract: [
28
30
  ' - ',
29
- (l, r) => ({
31
+ (state, l, r) => ({
30
32
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
31
- code: `matrix.subtract(${l.code}, ${r.code})`,
33
+ code: `${globalFnName(state, 'matrix')}.subtract(${l.code}, ${r.code})`,
32
34
  }),
33
35
  ],
34
36
  multiply: [
35
37
  ' * ',
36
- (l, r) => ({
38
+ (state, l, r) => ({
37
39
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
38
- code: `matrix.multiply(${l.code}, ${r.code})`,
40
+ code: `${globalFnName(state, 'matrix')}.multiply(${l.code}, ${r.code})`,
39
41
  }),
40
42
  ],
41
43
  dotMultiply: [
42
44
  ' * ',
43
- (l, r) => ({
45
+ (state, l, r) => ({
44
46
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
45
- code: `matrix.entrywise_multiply(${l.code}, ${r.code})`,
47
+ code: `${globalFnName(state, 'matrix')}.entrywise_multiply(${l.code}, ${r.code})`,
46
48
  }),
47
49
  ],
48
50
  divide: [
49
51
  ' / ',
50
- (l, r) => ({
51
- type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
52
- code: `matrix.multiply(${l.code}, matrix.invert(${r.code}))`,
53
- }),
52
+ (state, l, r) => {
53
+ if (r.type === 'array' || !r.type) {
54
+ return {
55
+ type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
56
+ code: `${globalFnName(state, 'matrix')}.multiply(${l.code}, ${globalFnName(state, 'matrix')}.invert(${r.code}))`,
57
+ };
58
+ }
59
+ return {
60
+ type: l.type === 'array' ? 'array' : l.type ? 'number' : undefined,
61
+ code: `${globalFnName(state, 'matrix')}.entrywise_divide(${l.code}, ${r.code})`,
62
+ };
63
+ },
54
64
  ],
55
65
  dotDivide: [
56
66
  ' / ',
57
- (l, r) => ({
67
+ (state, l, r) => ({
58
68
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
59
- code: `matrix.entrywise_divide(${l.code}, ${r.code})`,
69
+ code: `${globalFnName(state, 'matrix')}.entrywise_divide(${l.code}, ${r.code})`,
60
70
  }),
61
71
  ],
62
72
  mod: [' % '],
63
73
  pow: ['^', false],
64
74
  dotPow: ['^'],
65
- } satisfies Record<string, [op: string, alt?: ((l: Result, r: Result) => Result) | false]>;
75
+ } satisfies Record<string, [op: string, alt?: ((state: State, l: Result, r: Result) => Result) | false]>;
66
76
 
67
77
  const MATH_FUNCTIONS = {
68
78
  factorial: 'factorial',
@@ -76,70 +86,92 @@ const MATH_FUNCTIONS = {
76
86
  } as const;
77
87
 
78
88
  const BIT_OPS_TO_BOOL_OPS = {
79
- bitAnd: '&&',
80
- bitOr: '||',
81
- bitXor: '!=',
89
+ bitAnd: ['&&', false],
90
+ bitOr: ['||', false],
91
+ bitXor: ['!=', true],
82
92
  } as const;
83
93
 
84
94
  const COMPARE_OPERATORS = {
85
- smaller: '<',
86
- smallerEq: '<=',
87
- larger: '>',
88
- largerEq: '>=',
95
+ smaller: ['<'],
96
+ smallerEq: ['<='],
97
+ larger: ['>'],
98
+ largerEq: ['>='],
99
+ equal: ['=~', '=='],
100
+ unequal: ['!~', '!='],
89
101
  } as const;
90
102
 
91
103
  /** 转换为 boolean */
92
- function b(op: string, state: State, node: MathNode): string {
93
- const re = migrateExpr(state, node);
104
+ function b(op: string, state: State, node: MathNode, migrator = migrateExpr): string {
105
+ const re = migrator(state, node);
94
106
  return toBoolean(state, scalar(op, state, re)).code;
95
107
  }
96
108
 
109
+ /** 数组元素类型 */
110
+ function elementType(lit: VmAny): TypeName | undefined {
111
+ if (!isVmArray(lit) || !lit.length) return undefined;
112
+ let type: TypeName | undefined = undefined;
113
+ for (const e of lit) {
114
+ const t = operations.$Type(e as VmAny);
115
+ if (!type) {
116
+ type = t;
117
+ } else if (type !== t) {
118
+ return undefined;
119
+ }
120
+ }
121
+ return type;
122
+ }
123
+
97
124
  /** 二元操作 */
98
125
  function binary(
99
126
  state: State,
100
127
  l: Result,
101
128
  r: Result,
102
- op: (l: Result, r: Result) => Result,
103
- alt?: (l: Result, r: Result) => Result,
129
+ op: (state: State, l: Result, r: Result) => Result,
130
+ alt?: (state: State, l: Result, r: Result) => Result,
104
131
  ): Result {
105
132
  if (l.type && r.type && l.type !== 'array' && r.type !== 'array') {
106
- return op(l, r);
133
+ return op(state, l, r);
107
134
  }
108
135
  if (Array.isArray(l.literal) && l.literal.every((e) => !Array.isArray(e))) {
136
+ const it = { code: 'it', type: elementType(l.literal) };
109
137
  return {
110
138
  type: 'array',
111
- code: `${l.code}::map(fn { ${op({ code: 'it' }, r).code} })`,
139
+ code: `${l.code}::${globalFnName(state, 'map')}(fn { ${op(state, it, r).code} })`,
112
140
  };
113
141
  }
114
142
  if (Array.isArray(r.literal) && r.literal.every((e) => !Array.isArray(e))) {
143
+ const it = { code: 'it', type: elementType(r.literal) };
115
144
  return {
116
145
  type: 'array',
117
- code: `${r.code}::map(fn { ${op(l, { code: 'it' }).code} })`,
146
+ code: `${r.code}::${globalFnName(state, 'map')}(fn { ${op(state, l, it).code} })`,
118
147
  };
119
148
  }
120
149
  if (alt) {
121
- return alt(l, r);
150
+ return alt(state, l, r);
122
151
  }
152
+ const a = { code: 'a', type: l.type === 'array' ? elementType(l.literal) : l.type };
153
+ const b = { code: 'b', type: r.type === 'array' ? elementType(r.literal) : r.type };
123
154
  return {
124
155
  type: l.type === 'array' || r.type === 'array' ? 'array' : undefined,
125
- code: `matrix.entrywise(${l.code}, ${r.code}, fn (a, b) { ${op({ code: 'a' }, { code: 'b' }).code} })`,
156
+ code: `${globalFnName(state, 'matrix')}.entrywise(${l.code}, ${r.code}, fn (a, b) { ${op(state, a, b).code} })`,
126
157
  };
127
158
  }
128
159
 
129
160
  /** 一元操作 */
130
- function unary(state: State, v: Result, op: (v: Result) => Result): Result {
161
+ function unary(state: State, v: Result, op: (state: State, v: Result) => Result): Result {
131
162
  if (v.type && v.type !== 'array') {
132
- return op(v);
163
+ return op(state, v);
133
164
  }
134
165
  if (Array.isArray(v.literal) && v.literal.every((e) => !Array.isArray(e))) {
166
+ const it = { code: 'it', type: elementType(v.literal) };
135
167
  return {
136
168
  type: 'array',
137
- code: `${v.code}::map(fn { ${op({ code: 'it' }).code} })`,
169
+ code: `${v.code}::${globalFnName(state, 'map')}(fn { ${op(state, it).code} })`,
138
170
  };
139
171
  }
140
172
  return {
141
173
  type: 'array',
142
- code: `matrix.entrywise(${v.code}, nil, fn { ${op({ code: 'it' }).code} })`,
174
+ code: `${globalFnName(state, 'matrix')}.entrywise(${v.code}, nil, fn { ${op(state, { code: 'it' }).code} })`,
143
175
  };
144
176
  }
145
177
 
@@ -171,14 +203,14 @@ export function migrateOperator(
171
203
  state,
172
204
  migrateExpr(state, a0),
173
205
  migrateExpr(state, a1),
174
- (l, r) => ({
206
+ (state, l, r) => ({
175
207
  type: 'number',
176
208
  code: `${l.code}${op}${r.code}`,
177
209
  }),
178
210
  alt === false
179
- ? (l, r) => {
211
+ ? (state, l, r) => {
180
212
  state.warn(`'${op.trim()}' 不支持矩阵,计算结果可能不一致`);
181
- return { code: `${l.code} ^ ${r.code}` };
213
+ return { code: `${l.code}${op}${r.code}` };
182
214
  }
183
215
  : alt,
184
216
  );
@@ -187,9 +219,9 @@ export function migrateOperator(
187
219
  case 'unaryMinus':
188
220
  case 'unaryPlus': {
189
221
  const op = fn === 'unaryMinus' ? '-' : '+';
190
- const f = fn === 'unaryMinus' ? operations.$Neg : operations.$Pos;
191
222
  const exp = migrateExpr(state, a0);
192
223
  if (typeof exp.literal == 'number') {
224
+ const f = fn === 'unaryMinus' ? operations.$Neg : operations.$Pos;
193
225
  const v = f(exp.literal);
194
226
  return {
195
227
  type: 'number',
@@ -197,7 +229,7 @@ export function migrateOperator(
197
229
  code: serialize(v),
198
230
  };
199
231
  }
200
- return unary(state, migrateExpr(state, a0), (v) => ({
232
+ return unary(state, migrateExpr(state, a0), (state, v) => ({
201
233
  type: 'number',
202
234
  code: `${op}${v.code}`,
203
235
  }));
@@ -217,8 +249,10 @@ export function migrateOperator(
217
249
  return scalar(f, state, r);
218
250
  });
219
251
  if (codes.every((c) => c.type === 'boolean' || c.as_boolean) && fn in BIT_OPS_TO_BOOL_OPS) {
220
- const boolOp = BIT_OPS_TO_BOOL_OPS[fn as keyof typeof BIT_OPS_TO_BOOL_OPS];
221
- const code = codes.map((c) => toBoolean(state, c).code).join(` ${boolOp} `);
252
+ const [boolOp, needParen] = BIT_OPS_TO_BOOL_OPS[fn as keyof typeof BIT_OPS_TO_BOOL_OPS];
253
+ const code = args
254
+ .map((a) => toBoolean(state, (needParen ? migrateParen : migrateExpr)(state, a)).code)
255
+ .join(` ${boolOp} `);
222
256
  return {
223
257
  type: 'number',
224
258
  code: `to_number(${code})`,
@@ -253,7 +287,7 @@ export function migrateOperator(
253
287
  case 'xor': {
254
288
  return {
255
289
  type: 'boolean',
256
- code: `${open}${b('!=', state, a0)} != ${b('!=', state, a1)}${close}`,
290
+ code: `${open}${b('!=', state, a0, migrateParen)} != ${b('!=', state, a1, migrateParen)}${close}`,
257
291
  };
258
292
  }
259
293
  case 'not': {
@@ -279,17 +313,21 @@ export function migrateOperator(
279
313
  }
280
314
 
281
315
  case 'equal':
282
- case 'unequal': {
283
- const op = fn === 'equal' ? '==' : '!=';
316
+ case 'unequal':
317
+ case 'smaller':
318
+ case 'smallerEq':
319
+ case 'larger':
320
+ case 'largerEq': {
321
+ const [op, eqOp] = COMPARE_OPERATORS[fn];
284
322
  const c0 = constantValue(a0);
285
323
  const c1 = constantValue(a1);
286
- if (c0 === null || c1 === null) {
287
- state.loose();
288
- const p0 = c0 === null ? { code: `nil` } : scalar(op, state, migrateExpr(state, a0));
289
- const p1 = c1 === null ? { code: `nil` } : scalar(op, state, migrateExpr(state, a1));
324
+ if (eqOp && (c0 === null || c1 === null)) {
325
+ // Mathjs 只支持标量与 null 比较
326
+ const p0 = c0 === null ? { code: `nil` } : migrateExpr(state, a0);
327
+ const p1 = c1 === null ? { code: `nil` } : migrateExpr(state, a1);
290
328
  return {
291
329
  type: 'boolean',
292
- code: `${open}${p0.code} ${op} ${p1.code}${close}`,
330
+ code: `${open}${p0.code} ${eqOp} ${p1.code}${close}`,
293
331
  };
294
332
  }
295
333
  // PI, E 与常量比较时,不进行转换
@@ -307,23 +345,31 @@ export function migrateOperator(
307
345
  return r;
308
346
  };
309
347
 
310
- return binary(state, a(a0), a(a1), (l, r) => {
348
+ return binary(state, a(a0), a(a1), (state, l, r) => {
349
+ if (l.literal !== undefined) {
350
+ return {
351
+ type: 'boolean',
352
+ code: `${toNumber(state, l).code} ${op} ${r.code}`,
353
+ };
354
+ }
355
+ if (r.literal !== undefined) {
356
+ return {
357
+ type: 'boolean',
358
+ code: `${l.code} ${op} ${toNumber(state, r).code}`,
359
+ };
360
+ }
361
+ if (l.type === 'number' || r.type === 'number') {
362
+ return {
363
+ type: 'boolean',
364
+ code: `${l.code} ${op} ${r.code}`,
365
+ };
366
+ }
311
367
  return {
312
368
  type: 'boolean',
313
369
  code: `${toNumber(state, l).code} ${op} ${toNumber(state, r).code}`,
314
370
  };
315
371
  });
316
372
  }
317
- case 'smaller':
318
- case 'smallerEq':
319
- case 'larger':
320
- case 'largerEq': {
321
- const op = COMPARE_OPERATORS[fn];
322
- return {
323
- type: 'boolean',
324
- code: `${open}${scalar(op, state, migrateExpr(state, a0)).code} ${op} ${scalar(op, state, migrateExpr(state, a1)).code}${close}`,
325
- };
326
- }
327
373
 
328
374
  default: {
329
375
  state.err(`不支持的运算符: ${fn}`);
@@ -0,0 +1,21 @@
1
+ import type { VmValue } from '@mirascript/mirascript';
2
+ import { serialize as s, serializeNumber } from '@mirascript/mirascript/subtle';
3
+
4
+ /** 序列化 */
5
+ export function serialize(value: VmValue): string {
6
+ return s(value, {
7
+ serializeNumber: (value: number): string => {
8
+ if (!Number.isFinite(value) || value === 0) {
9
+ return serializeNumber(value);
10
+ }
11
+ const p1 = value.toString();
12
+ const p2 = value.toExponential();
13
+ // 还是优先选择默认的形式,除非指数形式更短很多
14
+ if (p2.length + 2 < p1.length) {
15
+ return p2;
16
+ } else {
17
+ return p1;
18
+ }
19
+ },
20
+ });
21
+ }
@@ -12,6 +12,37 @@ const pairs: ReadonlyArray<{ find: string; mathjs: MathNode; mira: string }> = (
12
12
  let d2 = matrix.diagonal([$yc, $yc, $yc], +3);
13
13
  d0::matrix.add(d1)::matrix.add(d2)`,
14
14
  ],
15
+ // 长导线校正移到后台处理
16
+ [
17
+ 'sinh(gamma',
18
+ `rpl = $R1pu * $Vbase * $Vbase / $Sbase; xpl = $X1pu * $Vbase * $Vbase / $Sbase;bpl = $B1pu / $Vbase / $Vbase * $Sbase; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); z = $Length * zp * (gamma == 0 ? 1 : (sinh(gamma * $Length)/(gamma * $Length))); re(z)`,
19
+ '$R1pu * $Vbase * $Vbase / $Sbase * $Length',
20
+ ],
21
+ [
22
+ 'sinh(gamma',
23
+ `rpl = $R1pu * $Vbase * $Vbase / $Sbase; xpl = $X1pu * $Vbase * $Vbase / $Sbase;bpl = $B1pu / $Vbase / $Vbase * $Sbase; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); z = $Length * zp * (gamma == 0 ? 1 : (sinh(gamma * $Length)/(gamma * $Length))); im(z)`,
24
+ '$X1pu * $Vbase * $Vbase / $Sbase * $Length',
25
+ ],
26
+ [
27
+ 'tanh(gamma',
28
+ `rpl = $R1pu * $Vbase * $Vbase / $Sbase; xpl = $X1pu * $Vbase * $Vbase / $Sbase;bpl = $B1pu / $Vbase / $Vbase * $Sbase; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); y = $Length * yp * (gamma == 0 ? 1 : (tanh(gamma * $Length / 2) / (gamma * $Length / 2))); im(y)`,
29
+ '$B1pu / $Vbase / $Vbase * $Sbase * $Length',
30
+ ],
31
+ [
32
+ 'sinh(gamma',
33
+ `rpl = $R1; xpl = $Xl1; bpl = 1e-6 / $Xc1; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); z = $Length * zp * (gamma == 0 ? 1 : (sinh(gamma * $Length)/(gamma * $Length))); re(z)`,
34
+ '$R1 * $Length',
35
+ ],
36
+ [
37
+ 'sinh(gamma',
38
+ `rpl = $R1; xpl = $Xl1; bpl = 1e-6 / $Xc1; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); z = $Length * zp * (gamma == 0 ? 1 : (sinh(gamma * $Length)/(gamma * $Length))); im(z)`,
39
+ '$Xl1 * $Length',
40
+ ],
41
+ [
42
+ 'tanh(gamma',
43
+ `rpl = $R1; xpl = $Xl1; bpl = 1e-6 / $Xc1; zp = rpl + i * xpl; yp = i * bpl; gamma = sqrt(zp * yp); y = $Length * yp * (gamma == 0 ? 1 : (tanh(gamma * $Length / 2) / (gamma * $Length / 2))); im(y)`,
44
+ '1e-6 / $Xc1 * $Length',
45
+ ],
15
46
  ] satisfies ReadonlyArray<[find: string, mathjs: string, mira: string]>
16
47
  ).map(([find, mathjs, mira]) => ({
17
48
  find,