@cloudpss/expression 0.6.0-alpha.2 → 0.6.0-alpha.20

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 (169) hide show
  1. package/dist/analyze.d.ts +6 -3
  2. package/dist/analyze.d.ts.map +1 -1
  3. package/dist/analyze.js +67 -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-decoration.d.ts +3 -3
  12. package/dist/definitions/parameter-decoration.d.ts.map +1 -1
  13. package/dist/definitions/parameter-decoration.js +0 -7
  14. package/dist/definitions/parameter-decoration.js.map +1 -1
  15. package/dist/definitions/parameter-group.d.ts +0 -4
  16. package/dist/definitions/parameter-group.d.ts.map +1 -1
  17. package/dist/definitions/parameter-group.js +5 -17
  18. package/dist/definitions/parameter-group.js.map +1 -1
  19. package/dist/definitions/parameter.d.ts +47 -40
  20. package/dist/definitions/parameter.d.ts.map +1 -1
  21. package/dist/definitions/parameter.js +9 -21
  22. package/dist/definitions/parameter.js.map +1 -1
  23. package/dist/definitions/utils.d.ts +28 -0
  24. package/dist/definitions/utils.d.ts.map +1 -0
  25. package/dist/definitions/utils.js +209 -0
  26. package/dist/definitions/utils.js.map +1 -0
  27. package/dist/definitions/variable.d.ts +0 -2
  28. package/dist/definitions/variable.d.ts.map +1 -1
  29. package/dist/definitions/variable.js +1 -5
  30. package/dist/definitions/variable.js.map +1 -1
  31. package/dist/definitions.d.ts +1 -0
  32. package/dist/definitions.d.ts.map +1 -1
  33. package/dist/definitions.js +1 -0
  34. package/dist/definitions.js.map +1 -1
  35. package/dist/eval.d.ts +3 -5
  36. package/dist/eval.d.ts.map +1 -1
  37. package/dist/eval.js +12 -20
  38. package/dist/eval.js.map +1 -1
  39. package/dist/expression.d.ts +10 -4
  40. package/dist/expression.d.ts.map +1 -1
  41. package/dist/expression.js +6 -6
  42. package/dist/expression.js.map +1 -1
  43. package/dist/index.d.ts +3 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/interface.d.ts +30 -0
  48. package/dist/interface.d.ts.map +1 -0
  49. package/dist/interface.js +6 -0
  50. package/dist/interface.js.map +1 -0
  51. package/dist/main.d.ts +41 -28
  52. package/dist/main.d.ts.map +1 -1
  53. package/dist/main.js +149 -147
  54. package/dist/main.js.map +1 -1
  55. package/dist/migrate.d.ts +3 -2
  56. package/dist/migrate.d.ts.map +1 -1
  57. package/dist/migrate.js +25 -3
  58. package/dist/migrate.js.map +1 -1
  59. package/dist/migrator/access.d.ts.map +1 -1
  60. package/dist/migrator/access.js +45 -25
  61. package/dist/migrator/access.js.map +1 -1
  62. package/dist/migrator/call.d.ts.map +1 -1
  63. package/dist/migrator/call.js +298 -201
  64. package/dist/migrator/call.js.map +1 -1
  65. package/dist/migrator/concat.d.ts.map +1 -1
  66. package/dist/migrator/concat.js +15 -2
  67. package/dist/migrator/concat.js.map +1 -1
  68. package/dist/migrator/function.d.ts +6 -0
  69. package/dist/migrator/function.d.ts.map +1 -0
  70. package/dist/migrator/function.js +25 -0
  71. package/dist/migrator/function.js.map +1 -0
  72. package/dist/migrator/interface.d.ts +3 -1
  73. package/dist/migrator/interface.d.ts.map +1 -1
  74. package/dist/migrator/interface.js.map +1 -1
  75. package/dist/migrator/node.d.ts.map +1 -1
  76. package/dist/migrator/node.js +35 -6
  77. package/dist/migrator/node.js.map +1 -1
  78. package/dist/migrator/operator.d.ts.map +1 -1
  79. package/dist/migrator/operator.js +107 -60
  80. package/dist/migrator/operator.js.map +1 -1
  81. package/dist/migrator/serialize.d.ts +4 -0
  82. package/dist/migrator/serialize.d.ts.map +1 -0
  83. package/dist/migrator/serialize.js +21 -0
  84. package/dist/migrator/serialize.js.map +1 -0
  85. package/dist/migrator/special.d.ts.map +1 -1
  86. package/dist/migrator/special.js +31 -0
  87. package/dist/migrator/special.js.map +1 -1
  88. package/dist/migrator/state.d.ts +2 -2
  89. package/dist/migrator/state.d.ts.map +1 -1
  90. package/dist/migrator/state.js +30 -33
  91. package/dist/migrator/state.js.map +1 -1
  92. package/dist/migrator/symbol.d.ts.map +1 -1
  93. package/dist/migrator/symbol.js +23 -9
  94. package/dist/migrator/symbol.js.map +1 -1
  95. package/dist/migrator/to-type.d.ts.map +1 -1
  96. package/dist/migrator/to-type.js +21 -4
  97. package/dist/migrator/to-type.js.map +1 -1
  98. package/dist/migrator/utils.d.ts +6 -0
  99. package/dist/migrator/utils.d.ts.map +1 -1
  100. package/dist/migrator/utils.js +35 -0
  101. package/dist/migrator/utils.js.map +1 -1
  102. package/dist/parser.d.ts +2 -2
  103. package/dist/parser.d.ts.map +1 -1
  104. package/dist/parser.js +27 -8
  105. package/dist/parser.js.map +1 -1
  106. package/dist/re-exports.d.ts +4 -0
  107. package/dist/re-exports.d.ts.map +1 -0
  108. package/dist/re-exports.js +3 -0
  109. package/dist/re-exports.js.map +1 -0
  110. package/dist/scope.d.ts +17 -18
  111. package/dist/scope.d.ts.map +1 -1
  112. package/dist/scope.js +84 -53
  113. package/dist/scope.js.map +1 -1
  114. package/dist/type.d.ts +27 -6
  115. package/dist/type.d.ts.map +1 -1
  116. package/dist/type.js +34 -15
  117. package/dist/type.js.map +1 -1
  118. package/package.json +8 -5
  119. package/src/analyze.ts +77 -37
  120. package/src/definitions/argument.ts +3 -13
  121. package/src/definitions/constraint.ts +3 -3
  122. package/src/definitions/parameter-decoration.ts +3 -9
  123. package/src/definitions/parameter-group.ts +4 -19
  124. package/src/definitions/parameter.ts +70 -62
  125. package/src/definitions/utils.ts +224 -0
  126. package/src/definitions/variable.ts +1 -6
  127. package/src/definitions.ts +1 -0
  128. package/src/eval.ts +16 -25
  129. package/src/expression.ts +16 -8
  130. package/src/index.ts +3 -1
  131. package/src/interface.ts +35 -0
  132. package/src/main.ts +217 -199
  133. package/src/migrate.ts +30 -6
  134. package/src/migrator/access.ts +50 -33
  135. package/src/migrator/call.ts +287 -190
  136. package/src/migrator/concat.ts +15 -2
  137. package/src/migrator/function.ts +27 -0
  138. package/src/migrator/interface.ts +3 -1
  139. package/src/migrator/node.ts +36 -5
  140. package/src/migrator/operator.ts +110 -64
  141. package/src/migrator/serialize.ts +21 -0
  142. package/src/migrator/special.ts +31 -0
  143. package/src/migrator/state.ts +30 -33
  144. package/src/migrator/symbol.ts +23 -9
  145. package/src/migrator/to-type.ts +23 -4
  146. package/src/migrator/utils.ts +38 -0
  147. package/src/parser.ts +33 -8
  148. package/src/re-exports.ts +47 -0
  149. package/src/scope.ts +101 -65
  150. package/src/type.ts +52 -18
  151. package/tests/analyze.ts +40 -6
  152. package/tests/compile.ts +65 -0
  153. package/tests/condition.ts +33 -17
  154. package/tests/definition.ts +237 -18
  155. package/tests/eval-complex.ts +19 -11
  156. package/tests/eval.ts +59 -12
  157. package/tests/import.ts +21 -7
  158. package/tests/main.ts +58 -0
  159. package/tests/migrate.ts +308 -0
  160. package/tests/scope.ts +3 -3
  161. package/tests/template.ts +36 -0
  162. package/dist/context.d.ts +0 -41
  163. package/dist/context.d.ts.map +0 -1
  164. package/dist/context.js +0 -18
  165. package/dist/context.js.map +0 -1
  166. package/jest.config.js +0 -3
  167. package/src/context.ts +0 -54
  168. package/tests/tsconfig.json +0 -3
  169. package/tsconfig.json +0 -3
@@ -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,20 @@ import {
12
12
  isRelationalNode,
13
13
  type OperatorNodeFn,
14
14
  isSymbolNode,
15
+ isFunctionAssignmentNode,
15
16
  } from 'mathjs';
16
17
  import type { State } from './state.js';
17
18
  import type { Options, Result } from './interface.js';
18
19
  import type { VmConst } from '@mirascript/mirascript';
19
- import { unsupportedNode } from './utils.js';
20
+ import { constantValue, unsupportedNode } from './utils.js';
20
21
  import { migrateAccess } from './access.js';
21
22
  import { migrateCall } from './call.js';
22
23
  import { migrateOperator } from './operator.js';
23
24
  import { migrateSymbol } from './symbol.js';
24
25
  import { migrateCondition } from './condition.js';
26
+ import { migrateFunctionAssignment } from './function.js';
27
+ import { serialize } from './serialize.js';
28
+ import { toNumber } from './to-type.js';
25
29
 
26
30
  /** 转换 AST */
27
31
  export function migrateAtomic(state: State, node: MathNode): Result {
@@ -79,7 +83,7 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
79
83
  } else {
80
84
  literals[key] = item.literal;
81
85
  }
82
- code.push(`${serializePropName(key)}: ${item.code}`);
86
+ code.push(`${serializeRecordKey(key)}: ${item.code}`);
83
87
  }
84
88
  return {
85
89
  type: 'record',
@@ -110,6 +114,26 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
110
114
  }
111
115
  if (isRelationalNode(node)) {
112
116
  const { conditionals, params } = node;
117
+ // 优化范围判断 a <= b <= c
118
+ if (
119
+ conditionals.length === 2 &&
120
+ params.length === 3 &&
121
+ conditionals[0] === 'smallerEq' &&
122
+ conditionals[1] === 'smallerEq'
123
+ ) {
124
+ // 模式匹配只支持常量
125
+ const l = constantValue(params[0]!);
126
+ const r = constantValue(params[2]!);
127
+ if (typeof l == 'number' && typeof r == 'number' && l < r) {
128
+ const v = toNumber(state, migrateNode(state, params[1]!, options));
129
+ let code = `${v.code} is ${l}..${r}`;
130
+ if (options.format !== 'no-paren') code = `(${code})`;
131
+ return {
132
+ type: 'boolean',
133
+ code,
134
+ };
135
+ }
136
+ }
113
137
  const exprs = [];
114
138
  for (let i = 0; i < conditionals.length; i++) {
115
139
  const fn = conditionals[i]! as OperatorNodeFn;
@@ -117,15 +141,22 @@ export function migrateNode(state: State, node: MathNode, options: Options): Res
117
141
  const r = params[i + 1]!;
118
142
  exprs.push(migrateOperator(state, { fn, args: [l, r] }, options));
119
143
  }
120
- let code = exprs.join(' && ');
144
+ let code = exprs.map((e) => e.code).join(' && ');
121
145
  if (options.format !== 'no-paren') code = `(${code})`;
122
146
  return {
123
147
  type: 'boolean',
124
148
  code,
125
149
  };
126
150
  }
151
+ if (isFunctionAssignmentNode(node)) {
152
+ const result = migrateFunctionAssignment(state, node);
153
+ state.helper(result.code);
154
+ return {
155
+ type: 'function',
156
+ code: node.name,
157
+ };
158
+ }
127
159
  // if (isRangeNode(node)) {
128
160
  // if (isIndexNode(node)) {
129
- // if (isFunctionAssignmentNode(node)) {
130
161
  return unsupportedNode(state, node);
131
162
  }
@@ -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,
@@ -1,10 +1,10 @@
1
- import { createVmContext, type VmContext } from '@mirascript/mirascript';
1
+ import type { VmContext } from '@mirascript/mirascript';
2
2
  import { parse } from './parser.js';
3
3
  import {
4
4
  type AssignmentNode,
5
- type FunctionAssignmentNode,
6
5
  isAssignmentNode,
7
6
  isBlockNode,
7
+ isConstantNode,
8
8
  isFunctionAssignmentNode,
9
9
  isSymbolNode,
10
10
  type MathNode,
@@ -12,9 +12,10 @@ import {
12
12
  import type { Result } from './interface.js';
13
13
  import { migrateAtomic } from './node.js';
14
14
  import { toBoolean } from './to-type.js';
15
- import { constantValue, symbolName, unsupportedNode } from './utils.js';
15
+ import { constantValue, migrateSymbolName, symbolName, unsupportedNode } from './utils.js';
16
16
  import { migrateAccess } from './access.js';
17
17
  import { specialMigrate } from './special.js';
18
+ import { migrateFunctionAssignment } from './function.js';
18
19
 
19
20
  /** 转换 AST */
20
21
  function migrateLast(state: State, node: MathNode): Result {
@@ -38,17 +39,20 @@ function migrateAssignment(state: State, node: AssignmentNode): Result {
38
39
  const { object, index, value } = node;
39
40
  const name = symbolName(object);
40
41
  if (name && index == null) {
41
- // name = xxx;
42
+ const localName = migrateSymbolName(name, true);
42
43
  if (state.locals.has(name)) {
43
- state.err(`不支持局部变量重新赋值: ${name}'`);
44
+ state.err(`不支持局部变量重新赋值: '${name}'`);
44
45
  return {
45
- code: `${name} = ${migrateAtomic(state, value).code}`,
46
+ code: `${localName} = ${migrateAtomic(state, value).code}`,
46
47
  };
47
48
  }
48
49
  const expr = migrateAtomic(state, value);
49
50
  state.locals.set(name, expr);
51
+ if (name !== localName) {
52
+ state.warn(`变量名 '${name}' 是 MiraScript 关键字,已转换为 '${localName}'`);
53
+ }
50
54
  return {
51
- code: `let ${name} = ${expr.code};`,
55
+ code: `let ${localName} = ${expr.code};`,
52
56
  };
53
57
  }
54
58
  if (index != null) {
@@ -60,26 +64,6 @@ function migrateAssignment(state: State, node: AssignmentNode): Result {
60
64
  }
61
65
  return unsupportedNode(state, node);
62
66
  }
63
- /** 转换函数定义语句 */
64
- function migrateFunctionAssignment(state: State, node: FunctionAssignmentNode): Result {
65
- state.loose();
66
- const { name, params, expr } = node;
67
- if (state.locals.has(name)) {
68
- state.err(`重复定义函数: ${name}`);
69
- }
70
- const fState = new FunctionState(state, params);
71
- let body = migrateAtomic(fState, expr);
72
- if (!(body.code.startsWith('{') && body.code.endsWith('}'))) {
73
- body = { ...body, code: `{ ${body.code} }` };
74
- }
75
- state.locals.set(name, {
76
- type: 'function',
77
- code: body.code,
78
- });
79
- return {
80
- code: `fn ${name}(${params.join(', ')}) ${body.code}`,
81
- };
82
- }
83
67
 
84
68
  /** 转换状态 */
85
69
  export class BaseState {
@@ -87,7 +71,7 @@ export class BaseState {
87
71
  private readonly expr: string,
88
72
  readonly condition: boolean,
89
73
  /** 可识别的全局环境 */
90
- readonly globals: VmContext = createVmContext(),
74
+ readonly globals: VmContext,
91
75
  ) {}
92
76
  /** 帮助函数 */
93
77
  private readonly helpers = new Set<string>();
@@ -126,7 +110,7 @@ export class BaseState {
126
110
  const r = isFunc ? migrateFunctionAssignment(this, node) : migrator(this, node);
127
111
  let { code } = r;
128
112
  if (stmt && !isFunc && !code.endsWith(';')) code += ';';
129
- if (node.comment) code += ' //' + node.comment;
113
+ if (node.comment) code += ' //' + node.comment.slice(1);
130
114
  this.ret.push(code);
131
115
  }
132
116
  /** 转换 */
@@ -143,7 +127,6 @@ export class BaseState {
143
127
  const sp = specialMigrate(this.expr, ast);
144
128
  if (sp != null) {
145
129
  this.ret.push(sp);
146
- this.loose();
147
130
  return;
148
131
  }
149
132
  if (isBlockNode(ast)) {
@@ -160,6 +143,8 @@ export class BaseState {
160
143
  }
161
144
  }
162
145
  return;
146
+ } else if (isConstantNode(ast) && ast.value === undefined && ast.comment) {
147
+ this.ret.push(`//${ast.comment.slice(1)}`);
163
148
  } else {
164
149
  this.migrateStmt(migrateLast, false, ast);
165
150
  return;
@@ -178,21 +163,33 @@ export class BaseState {
178
163
  // 写入原始 math.js 作为注释
179
164
  const lines = this.expr.split('\n').map((line) => `// ${line}`);
180
165
  lines.unshift(`// # 原始 math.js 表达式`);
166
+ const addComment = (prefix: string, item: string) => {
167
+ if (!item.includes('\n')) {
168
+ lines.unshift(`// ${prefix}: ${item}`);
169
+ return;
170
+ }
171
+ const l = item.split('\n');
172
+ const indent = ' '.repeat(prefix.length + 2);
173
+ for (let i = l.length - 1; i >= 1; i--) {
174
+ lines.unshift(`// ${indent}${l[i]}`);
175
+ }
176
+ lines.unshift(`// ${prefix}: ${l[0]}`);
177
+ };
181
178
  if (this.errors.size || this.warnings.size || this.critical.size) {
182
179
  const warnings = [...this.warnings];
183
180
  for (let i = warnings.length - 1; i >= 0; i--) {
184
181
  const warn = warnings[i]!;
185
- lines.unshift(`// - W: ${warn}`);
182
+ addComment('- W', warn);
186
183
  }
187
184
  const errors = [...this.errors];
188
185
  for (let i = errors.length - 1; i >= 0; i--) {
189
186
  const err = errors[i]!;
190
- lines.unshift(`// - E: ${err}`);
187
+ addComment('- E', err);
191
188
  }
192
189
  const critical = [...this.critical];
193
190
  for (let i = critical.length - 1; i >= 0; i--) {
194
191
  const crit = critical[i]!;
195
- lines.unshift(`// - C: ${crit}`);
192
+ addComment('- C', crit);
196
193
  }
197
194
  lines.unshift(`// # 转换日志`);
198
195
  }