@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.
- package/dist/analyze.d.ts +6 -3
- package/dist/analyze.d.ts.map +1 -1
- package/dist/analyze.js +13 -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-group.js +4 -4
- package/dist/definitions/parameter-group.js.map +1 -1
- package/dist/definitions/parameter.d.ts +22 -14
- package/dist/definitions/parameter.d.ts.map +1 -1
- package/dist/definitions/parameter.js +10 -1
- 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 +272 -0
- package/dist/definitions/utils.js.map +1 -0
- package/dist/definitions/variable.js +1 -1
- package/dist/definitions/variable.js.map +1 -1
- package/dist/definitions.d.ts +1 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +1 -1
- 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 +165 -149
- 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 +65 -30
- 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 +55 -7
- 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 +34 -12
- 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 +49 -1
- 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 +22 -10
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +21 -24
- package/dist/type.js.map +1 -1
- package/package.json +8 -5
- package/src/analyze.ts +20 -39
- package/src/definitions/argument.ts +3 -13
- package/src/definitions/constraint.ts +3 -3
- package/src/definitions/parameter-group.ts +4 -4
- package/src/definitions/parameter.ts +47 -24
- package/src/definitions/utils.ts +288 -0
- package/src/definitions/variable.ts +1 -1
- package/src/definitions.ts +1 -28
- 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 +232 -200
- package/src/migrate.ts +30 -6
- package/src/migrator/access.ts +68 -38
- 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 +56 -6
- 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 +33 -12
- package/src/migrator/to-type.ts +23 -4
- package/src/migrator/utils.ts +49 -1
- package/src/parser.ts +33 -8
- package/src/re-exports.ts +47 -0
- package/src/scope.ts +101 -65
- package/src/type.ts +40 -25
- package/tests/analyze.ts +45 -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 +317 -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/src/context.ts +0 -54
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
|
}
|
package/src/migrator/symbol.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { SymbolNode } from 'mathjs';
|
|
|
2
2
|
import type { Result } from './interface.js';
|
|
3
3
|
import type { State } from './state.js';
|
|
4
4
|
import { operations } from '@mirascript/mirascript/subtle';
|
|
5
|
+
import { migrateSymbolName } from './utils.js';
|
|
5
6
|
|
|
6
7
|
/** 处理符号名称 */
|
|
7
8
|
export function migrateSymbol(state: State, node: SymbolNode, migrateConst: boolean): Result {
|
|
@@ -10,15 +11,23 @@ export function migrateSymbol(state: State, node: SymbolNode, migrateConst: bool
|
|
|
10
11
|
if (local) {
|
|
11
12
|
return {
|
|
12
13
|
...local,
|
|
13
|
-
code: name,
|
|
14
|
+
code: migrateSymbolName(name, true),
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
if (state.globals.has(name)) {
|
|
18
|
+
try {
|
|
19
|
+
const global = state.globals.get(name);
|
|
20
|
+
return {
|
|
21
|
+
type: operations.$Type(global),
|
|
22
|
+
code: migrateSymbolName(name, false),
|
|
23
|
+
global,
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return {
|
|
27
|
+
type: undefined,
|
|
28
|
+
code: migrateSymbolName(name, false),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
22
31
|
}
|
|
23
32
|
if (name.startsWith('$') || name.startsWith('@')) {
|
|
24
33
|
return {
|
|
@@ -33,21 +42,33 @@ export function migrateSymbol(state: State, node: SymbolNode, migrateConst: bool
|
|
|
33
42
|
code: `i`,
|
|
34
43
|
};
|
|
35
44
|
}
|
|
36
|
-
if (name === 'pi'
|
|
45
|
+
if (name === 'pi') {
|
|
37
46
|
return {
|
|
38
47
|
type: 'number',
|
|
39
|
-
code:
|
|
48
|
+
code: `PI`,
|
|
40
49
|
};
|
|
41
50
|
}
|
|
42
|
-
if (name === 'e'
|
|
51
|
+
if (name === 'e') {
|
|
43
52
|
return {
|
|
44
53
|
type: 'number',
|
|
45
|
-
code:
|
|
54
|
+
code: `E`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (name === '__PREVIEW') {
|
|
58
|
+
return {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
code: '@PREVIEW',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (name === '__ICON') {
|
|
64
|
+
return {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
code: '@ICON',
|
|
46
67
|
};
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
70
|
state.warn(`符号 '${name}' 未定义`);
|
|
50
71
|
return {
|
|
51
|
-
code: name,
|
|
72
|
+
code: migrateSymbolName(name, false),
|
|
52
73
|
};
|
|
53
74
|
}
|
package/src/migrator/to-type.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { isConstantNode, isNode, type MathNode } from 'mathjs';
|
|
2
2
|
import type { Result } from './interface.js';
|
|
3
|
-
import { serialize } from '
|
|
3
|
+
import { serialize } from './serialize.js';
|
|
4
4
|
import type { State } from './state.js';
|
|
5
5
|
import { migrateNode } from './node.js';
|
|
6
|
+
import { globalFnName } from './utils.js';
|
|
6
7
|
|
|
7
8
|
/** 转换 AST */
|
|
8
9
|
export function toBoolean(state: State, node: MathNode | Result): Result {
|
|
9
10
|
const helper = (): void => {
|
|
10
|
-
state.helper(
|
|
11
|
+
state.helper(
|
|
12
|
+
"fn @@to_boolean { if it is nil { nil } else { it != '' && it != '0' && it != 0 && it is not nan && it != false } }",
|
|
13
|
+
);
|
|
11
14
|
};
|
|
12
15
|
if (!isNode(node)) {
|
|
13
16
|
if (node.type === 'boolean') return node;
|
|
@@ -46,9 +49,17 @@ export function toString(state: State, node: MathNode | Result): Result {
|
|
|
46
49
|
code: node.as_string,
|
|
47
50
|
};
|
|
48
51
|
}
|
|
52
|
+
if (node.type === 'number' && node.literal !== undefined) {
|
|
53
|
+
const lit = String(node.literal);
|
|
54
|
+
return {
|
|
55
|
+
type: 'string',
|
|
56
|
+
literal: lit,
|
|
57
|
+
code: serialize(lit),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
49
60
|
return {
|
|
50
61
|
type: 'string',
|
|
51
|
-
code:
|
|
62
|
+
code: `${globalFnName(state, 'to_string')}(${node.code})`,
|
|
52
63
|
};
|
|
53
64
|
}
|
|
54
65
|
if (isConstantNode(node)) {
|
|
@@ -74,9 +85,17 @@ export function toNumber(state: State, node: MathNode | Result): Result {
|
|
|
74
85
|
code: node.as_number,
|
|
75
86
|
};
|
|
76
87
|
}
|
|
88
|
+
if ((node.type === 'string' || node.type === 'boolean') && node.literal !== undefined) {
|
|
89
|
+
const lit = Number(node.literal);
|
|
90
|
+
return {
|
|
91
|
+
type: 'number',
|
|
92
|
+
literal: lit,
|
|
93
|
+
code: serialize(lit),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
77
96
|
return {
|
|
78
97
|
type: 'number',
|
|
79
|
-
code:
|
|
98
|
+
code: `${globalFnName(state, 'to_number')}(${node.code})`,
|
|
80
99
|
};
|
|
81
100
|
}
|
|
82
101
|
if (isConstantNode(node)) {
|
package/src/migrator/utils.ts
CHANGED
|
@@ -1,17 +1,44 @@
|
|
|
1
|
-
import { type MathNode, isSymbolNode, isConstantNode } from 'mathjs';
|
|
1
|
+
import { type MathNode, isSymbolNode, isConstantNode, isOperatorNode } from 'mathjs';
|
|
2
|
+
import { keywords } from '@mirascript/mirascript/subtle';
|
|
2
3
|
import type { Result } from './interface.js';
|
|
3
4
|
import type { State } from './state.js';
|
|
4
5
|
import { toString } from './to-type.js';
|
|
5
6
|
import { migrateExpr } from './node.js';
|
|
6
7
|
|
|
8
|
+
/** 获取全局函数的名字 */
|
|
9
|
+
export function globalFnName(state: State, fnName: string): string {
|
|
10
|
+
if (state.locals.has(fnName)) {
|
|
11
|
+
return `global.${fnName}`;
|
|
12
|
+
}
|
|
13
|
+
return fnName;
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
/** 获取 symbol name */
|
|
8
17
|
export function symbolName(node: MathNode): string | undefined {
|
|
9
18
|
if (!isSymbolNode(node)) return undefined;
|
|
10
19
|
return node.name;
|
|
11
20
|
}
|
|
12
21
|
|
|
22
|
+
const kw = new Set(keywords());
|
|
23
|
+
/** 转换为 mirascript 符号 */
|
|
24
|
+
export function migrateSymbolName(name: string, local: boolean): string {
|
|
25
|
+
if (!kw.has(name)) return name;
|
|
26
|
+
if (local) return `__${name}__`;
|
|
27
|
+
return `global.${name}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
/** 获取 constant value */
|
|
14
31
|
export function constantValue(node: MathNode): number | string | boolean | null | undefined {
|
|
32
|
+
if (isOperatorNode(node) && node.args.length === 1) {
|
|
33
|
+
if (node.op === '+') {
|
|
34
|
+
const val = constantValue(node.args[0]!);
|
|
35
|
+
if (typeof val === 'number') return val;
|
|
36
|
+
} else if (node.op === '-') {
|
|
37
|
+
const val = constantValue(node.args[0]!);
|
|
38
|
+
if (typeof val === 'number') return -val;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
15
42
|
if (!isConstantNode(node)) return undefined;
|
|
16
43
|
return node.value ?? null;
|
|
17
44
|
}
|
|
@@ -65,3 +92,24 @@ export function equalText(state: State, op: '==' | '!=', l: MathNode, r: MathNod
|
|
|
65
92
|
code: `${toString(state, lr).code} ${op} ${toString(state, rr).code}`,
|
|
66
93
|
};
|
|
67
94
|
}
|
|
95
|
+
|
|
96
|
+
/** 计算长度 */
|
|
97
|
+
export function len(state: State, obj: Result): Result {
|
|
98
|
+
if (obj.type === 'string')
|
|
99
|
+
return {
|
|
100
|
+
type: 'number',
|
|
101
|
+
code: `len(chars(${obj.code}))`,
|
|
102
|
+
};
|
|
103
|
+
if (obj.type === 'array')
|
|
104
|
+
return {
|
|
105
|
+
type: 'number',
|
|
106
|
+
code: `len(${obj.code})`,
|
|
107
|
+
};
|
|
108
|
+
state.helper(
|
|
109
|
+
`fn @@length(x) { if type(x) == 'string' { len(chars(x)) } else if type(x) == 'array' { len(x) } else { x.length } }`,
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
type: 'number',
|
|
113
|
+
code: `@@length(${obj.code})`,
|
|
114
|
+
};
|
|
115
|
+
}
|
package/src/parser.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { compileSync, type VmScript } from '@mirascript/mirascript';
|
|
2
|
-
import
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
|
+
import type { Evaluator } from './main.js';
|
|
4
|
+
|
|
3
5
|
/** 一般表达式 */
|
|
4
6
|
export interface EvalExpressionCache {
|
|
5
7
|
/**
|
|
@@ -38,12 +40,12 @@ export type ExpressionCache = ErrorExpressionCache | EvalExpressionCache;
|
|
|
38
40
|
/**
|
|
39
41
|
* 编译表达式
|
|
40
42
|
*/
|
|
41
|
-
function compile(expression: string): ExpressionCache {
|
|
43
|
+
function compile(expression: string, template: boolean): ExpressionCache {
|
|
42
44
|
const compiled = {
|
|
43
45
|
source: expression,
|
|
44
46
|
} as ExpressionCache;
|
|
45
47
|
try {
|
|
46
|
-
const script = compileSync(expression);
|
|
48
|
+
const script = compileSync(expression, { input_mode: template ? 'Template' : 'Script' });
|
|
47
49
|
compiled.func = script;
|
|
48
50
|
} catch (ex) {
|
|
49
51
|
compiled.error = ex as Error;
|
|
@@ -51,19 +53,42 @@ function compile(expression: string): ExpressionCache {
|
|
|
51
53
|
return compiled;
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
const kTemplateCache = Symbol('cloudpss.expression.templateCache');
|
|
57
|
+
const kExpressionCache = Symbol('cloudpss.expression.expressionCache');
|
|
58
|
+
|
|
59
|
+
/** 获取编译缓存 */
|
|
60
|
+
function getCache(evaluator: Evaluator | null, template: boolean): LRUCache<string, EvalExpressionCache> | null {
|
|
61
|
+
if (evaluator == null) return null;
|
|
62
|
+
const key = template ? kTemplateCache : kExpressionCache;
|
|
63
|
+
if (!(key in evaluator)) {
|
|
64
|
+
const size = template ? evaluator.options.templateCacheSize : evaluator.options.expressionCacheSize;
|
|
65
|
+
Object.defineProperty(evaluator, key, {
|
|
66
|
+
value: new LRUCache<string, EvalExpressionCache>({ max: size! || 50 }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return (evaluator as Evaluator & Record<typeof key, LRUCache<string, EvalExpressionCache>>)[key];
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
/**
|
|
55
73
|
* 解析表达式
|
|
56
74
|
*/
|
|
57
|
-
export function parse(
|
|
75
|
+
export function parse(
|
|
76
|
+
evaluator: Evaluator | null,
|
|
77
|
+
expression: string,
|
|
78
|
+
throws: boolean,
|
|
79
|
+
template: boolean,
|
|
80
|
+
): ExpressionCache {
|
|
58
81
|
if (typeof expression != 'string') {
|
|
59
82
|
throw new TypeError(`${String(expression)} is not a valid expression`);
|
|
60
83
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
const cache = getCache(evaluator, template);
|
|
85
|
+
const cached = cache?.get(expression);
|
|
86
|
+
if (cached != null) return cached;
|
|
87
|
+
const complied = compile(expression, template);
|
|
64
88
|
if (complied.error) {
|
|
65
89
|
if (throws) throw complied.error;
|
|
90
|
+
} else {
|
|
91
|
+
cache?.set(expression, complied);
|
|
66
92
|
}
|
|
67
|
-
context.expressionCache.set(expression, complied);
|
|
68
93
|
return complied;
|
|
69
94
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export {
|
|
2
|
+
isVmAny,
|
|
3
|
+
isVmArray,
|
|
4
|
+
isVmConst,
|
|
5
|
+
isVmContext,
|
|
6
|
+
isVmExtern,
|
|
7
|
+
isVmFunction,
|
|
8
|
+
isVmImmutable,
|
|
9
|
+
isVmModule,
|
|
10
|
+
isVmPrimitive,
|
|
11
|
+
isVmRecord,
|
|
12
|
+
isVmArrayLikeRecord,
|
|
13
|
+
isVmArrayLikeRecordByEntires,
|
|
14
|
+
isVmArrayLikeRecordByKeys,
|
|
15
|
+
isVmCallable,
|
|
16
|
+
isVmScript,
|
|
17
|
+
isVmWrapper,
|
|
18
|
+
isVmValue,
|
|
19
|
+
VmExtern,
|
|
20
|
+
VmFunction,
|
|
21
|
+
VmModule,
|
|
22
|
+
VmError,
|
|
23
|
+
defineVmContextValue,
|
|
24
|
+
} from '@mirascript/mirascript';
|
|
25
|
+
export type {
|
|
26
|
+
VmScript,
|
|
27
|
+
VmValueMap,
|
|
28
|
+
VmAny,
|
|
29
|
+
VmArray,
|
|
30
|
+
VmConst,
|
|
31
|
+
VmContext,
|
|
32
|
+
VmImmutable,
|
|
33
|
+
VmPrimitive,
|
|
34
|
+
VmRecord,
|
|
35
|
+
VmUninitialized,
|
|
36
|
+
VmValue,
|
|
37
|
+
} from '@mirascript/mirascript';
|
|
38
|
+
export {
|
|
39
|
+
serialize,
|
|
40
|
+
serializeRecordKey,
|
|
41
|
+
serializeString,
|
|
42
|
+
type SerializeOptions,
|
|
43
|
+
constants,
|
|
44
|
+
DefaultVmContext,
|
|
45
|
+
lib,
|
|
46
|
+
operations,
|
|
47
|
+
} from '@mirascript/mirascript/subtle';
|