@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.
- package/dist/analyze.d.ts +6 -3
- package/dist/analyze.d.ts.map +1 -1
- package/dist/analyze.js +67 -33
- package/dist/analyze.js.map +1 -1
- package/dist/definitions/argument.d.ts +3 -11
- package/dist/definitions/argument.d.ts.map +1 -1
- package/dist/definitions/constraint.d.ts +2 -2
- package/dist/definitions/constraint.d.ts.map +1 -1
- package/dist/definitions/constraint.js +1 -1
- package/dist/definitions/constraint.js.map +1 -1
- package/dist/definitions/parameter-decoration.d.ts +3 -3
- package/dist/definitions/parameter-decoration.d.ts.map +1 -1
- package/dist/definitions/parameter-decoration.js +0 -7
- package/dist/definitions/parameter-decoration.js.map +1 -1
- package/dist/definitions/parameter-group.d.ts +0 -4
- package/dist/definitions/parameter-group.d.ts.map +1 -1
- package/dist/definitions/parameter-group.js +5 -17
- package/dist/definitions/parameter-group.js.map +1 -1
- package/dist/definitions/parameter.d.ts +47 -40
- package/dist/definitions/parameter.d.ts.map +1 -1
- package/dist/definitions/parameter.js +9 -21
- package/dist/definitions/parameter.js.map +1 -1
- package/dist/definitions/utils.d.ts +28 -0
- package/dist/definitions/utils.d.ts.map +1 -0
- package/dist/definitions/utils.js +209 -0
- package/dist/definitions/utils.js.map +1 -0
- package/dist/definitions/variable.d.ts +0 -2
- package/dist/definitions/variable.d.ts.map +1 -1
- package/dist/definitions/variable.js +1 -5
- package/dist/definitions/variable.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +1 -0
- package/dist/definitions.js.map +1 -1
- package/dist/eval.d.ts +3 -5
- package/dist/eval.d.ts.map +1 -1
- package/dist/eval.js +12 -20
- package/dist/eval.js.map +1 -1
- package/dist/expression.d.ts +10 -4
- package/dist/expression.d.ts.map +1 -1
- package/dist/expression.js +6 -6
- package/dist/expression.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interface.d.ts +30 -0
- package/dist/interface.d.ts.map +1 -0
- package/dist/interface.js +6 -0
- package/dist/interface.js.map +1 -0
- package/dist/main.d.ts +41 -28
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +149 -147
- package/dist/main.js.map +1 -1
- package/dist/migrate.d.ts +3 -2
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +25 -3
- package/dist/migrate.js.map +1 -1
- package/dist/migrator/access.d.ts.map +1 -1
- package/dist/migrator/access.js +45 -25
- package/dist/migrator/access.js.map +1 -1
- package/dist/migrator/call.d.ts.map +1 -1
- package/dist/migrator/call.js +298 -201
- package/dist/migrator/call.js.map +1 -1
- package/dist/migrator/concat.d.ts.map +1 -1
- package/dist/migrator/concat.js +15 -2
- package/dist/migrator/concat.js.map +1 -1
- package/dist/migrator/function.d.ts +6 -0
- package/dist/migrator/function.d.ts.map +1 -0
- package/dist/migrator/function.js +25 -0
- package/dist/migrator/function.js.map +1 -0
- package/dist/migrator/interface.d.ts +3 -1
- package/dist/migrator/interface.d.ts.map +1 -1
- package/dist/migrator/interface.js.map +1 -1
- package/dist/migrator/node.d.ts.map +1 -1
- package/dist/migrator/node.js +35 -6
- package/dist/migrator/node.js.map +1 -1
- package/dist/migrator/operator.d.ts.map +1 -1
- package/dist/migrator/operator.js +107 -60
- package/dist/migrator/operator.js.map +1 -1
- package/dist/migrator/serialize.d.ts +4 -0
- package/dist/migrator/serialize.d.ts.map +1 -0
- package/dist/migrator/serialize.js +21 -0
- package/dist/migrator/serialize.js.map +1 -0
- package/dist/migrator/special.d.ts.map +1 -1
- package/dist/migrator/special.js +31 -0
- package/dist/migrator/special.js.map +1 -1
- package/dist/migrator/state.d.ts +2 -2
- package/dist/migrator/state.d.ts.map +1 -1
- package/dist/migrator/state.js +30 -33
- package/dist/migrator/state.js.map +1 -1
- package/dist/migrator/symbol.d.ts.map +1 -1
- package/dist/migrator/symbol.js +23 -9
- package/dist/migrator/symbol.js.map +1 -1
- package/dist/migrator/to-type.d.ts.map +1 -1
- package/dist/migrator/to-type.js +21 -4
- package/dist/migrator/to-type.js.map +1 -1
- package/dist/migrator/utils.d.ts +6 -0
- package/dist/migrator/utils.d.ts.map +1 -1
- package/dist/migrator/utils.js +35 -0
- package/dist/migrator/utils.js.map +1 -1
- package/dist/parser.d.ts +2 -2
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +27 -8
- package/dist/parser.js.map +1 -1
- package/dist/re-exports.d.ts +4 -0
- package/dist/re-exports.d.ts.map +1 -0
- package/dist/re-exports.js +3 -0
- package/dist/re-exports.js.map +1 -0
- package/dist/scope.d.ts +17 -18
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +84 -53
- package/dist/scope.js.map +1 -1
- package/dist/type.d.ts +27 -6
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +34 -15
- package/dist/type.js.map +1 -1
- package/package.json +8 -5
- package/src/analyze.ts +77 -37
- package/src/definitions/argument.ts +3 -13
- package/src/definitions/constraint.ts +3 -3
- package/src/definitions/parameter-decoration.ts +3 -9
- package/src/definitions/parameter-group.ts +4 -19
- package/src/definitions/parameter.ts +70 -62
- package/src/definitions/utils.ts +224 -0
- package/src/definitions/variable.ts +1 -6
- package/src/definitions.ts +1 -0
- package/src/eval.ts +16 -25
- package/src/expression.ts +16 -8
- package/src/index.ts +3 -1
- package/src/interface.ts +35 -0
- package/src/main.ts +217 -199
- package/src/migrate.ts +30 -6
- package/src/migrator/access.ts +50 -33
- package/src/migrator/call.ts +287 -190
- package/src/migrator/concat.ts +15 -2
- package/src/migrator/function.ts +27 -0
- package/src/migrator/interface.ts +3 -1
- package/src/migrator/node.ts +36 -5
- package/src/migrator/operator.ts +110 -64
- package/src/migrator/serialize.ts +21 -0
- package/src/migrator/special.ts +31 -0
- package/src/migrator/state.ts +30 -33
- package/src/migrator/symbol.ts +23 -9
- package/src/migrator/to-type.ts +23 -4
- package/src/migrator/utils.ts +38 -0
- package/src/parser.ts +33 -8
- package/src/re-exports.ts +47 -0
- package/src/scope.ts +101 -65
- package/src/type.ts +52 -18
- package/tests/analyze.ts +40 -6
- package/tests/compile.ts +65 -0
- package/tests/condition.ts +33 -17
- package/tests/definition.ts +237 -18
- package/tests/eval-complex.ts +19 -11
- package/tests/eval.ts +59 -12
- package/tests/import.ts +21 -7
- package/tests/main.ts +58 -0
- package/tests/migrate.ts +308 -0
- package/tests/scope.ts +3 -3
- package/tests/template.ts +36 -0
- package/dist/context.d.ts +0 -41
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -18
- package/dist/context.js.map +0 -1
- package/jest.config.js +0 -3
- package/src/context.ts +0 -54
- package/tests/tsconfig.json +0 -3
- package/tsconfig.json +0 -3
|
@@ -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
|
/** 转换后的代码 */
|
package/src/migrator/node.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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(`${
|
|
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
|
}
|
package/src/migrator/operator.ts
CHANGED
|
@@ -8,61 +8,71 @@ import {
|
|
|
8
8
|
isOperatorNode,
|
|
9
9
|
isNode,
|
|
10
10
|
} from 'mathjs';
|
|
11
|
-
import {
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
47
|
+
code: `${globalFnName(state, 'matrix')}.entrywise_multiply(${l.code}, ${r.code})`,
|
|
46
48
|
}),
|
|
47
49
|
],
|
|
48
50
|
divide: [
|
|
49
51
|
' / ',
|
|
50
|
-
(l, r) =>
|
|
51
|
-
|
|
52
|
-
|
|
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:
|
|
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 =
|
|
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}
|
|
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}
|
|
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:
|
|
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}
|
|
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:
|
|
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}
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
288
|
-
const p0 = c0 === null ? { code: `nil` } :
|
|
289
|
-
const p1 = c1 === null ? { code: `nil` } :
|
|
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} ${
|
|
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
|
+
}
|
package/src/migrator/special.ts
CHANGED
|
@@ -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,
|
package/src/migrator/state.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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: `${
|
|
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 ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
+
addComment('- C', crit);
|
|
196
193
|
}
|
|
197
194
|
lines.unshift(`// # 转换日志`);
|
|
198
195
|
}
|