@cloudpss/expression 0.6.0-alpha.1 → 0.6.0-alpha.11

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 (154) 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 +2 -10
  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 +19 -0
  24. package/dist/definitions/utils.d.ts.map +1 -0
  25. package/dist/definitions/utils.js +177 -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 +4 -4
  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 +145 -141
  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 +44 -24
  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 +254 -217
  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/interface.d.ts +3 -1
  69. package/dist/migrator/interface.d.ts.map +1 -1
  70. package/dist/migrator/interface.js.map +1 -1
  71. package/dist/migrator/node.js +1 -1
  72. package/dist/migrator/node.js.map +1 -1
  73. package/dist/migrator/operator.d.ts.map +1 -1
  74. package/dist/migrator/operator.js +43 -29
  75. package/dist/migrator/operator.js.map +1 -1
  76. package/dist/migrator/state.d.ts +2 -2
  77. package/dist/migrator/state.d.ts.map +1 -1
  78. package/dist/migrator/state.js +1 -2
  79. package/dist/migrator/state.js.map +1 -1
  80. package/dist/migrator/symbol.d.ts.map +1 -1
  81. package/dist/migrator/symbol.js +13 -0
  82. package/dist/migrator/symbol.js.map +1 -1
  83. package/dist/migrator/to-type.d.ts.map +1 -1
  84. package/dist/migrator/to-type.js +20 -3
  85. package/dist/migrator/to-type.js.map +1 -1
  86. package/dist/migrator/utils.d.ts +4 -0
  87. package/dist/migrator/utils.d.ts.map +1 -1
  88. package/dist/migrator/utils.js +25 -0
  89. package/dist/migrator/utils.js.map +1 -1
  90. package/dist/parser.d.ts +2 -2
  91. package/dist/parser.d.ts.map +1 -1
  92. package/dist/parser.js +25 -8
  93. package/dist/parser.js.map +1 -1
  94. package/dist/re-exports.d.ts +4 -0
  95. package/dist/re-exports.d.ts.map +1 -0
  96. package/dist/re-exports.js +3 -0
  97. package/dist/re-exports.js.map +1 -0
  98. package/dist/scope.d.ts +13 -16
  99. package/dist/scope.d.ts.map +1 -1
  100. package/dist/scope.js +56 -49
  101. package/dist/scope.js.map +1 -1
  102. package/dist/type.d.ts +14 -5
  103. package/dist/type.d.ts.map +1 -1
  104. package/dist/type.js +35 -11
  105. package/dist/type.js.map +1 -1
  106. package/package.json +7 -4
  107. package/src/analyze.ts +77 -37
  108. package/src/definitions/argument.ts +2 -12
  109. package/src/definitions/constraint.ts +3 -3
  110. package/src/definitions/parameter-decoration.ts +3 -9
  111. package/src/definitions/parameter-group.ts +4 -19
  112. package/src/definitions/parameter.ts +62 -61
  113. package/src/definitions/utils.ts +175 -0
  114. package/src/definitions/variable.ts +1 -6
  115. package/src/definitions.ts +1 -0
  116. package/src/eval.ts +13 -26
  117. package/src/expression.ts +14 -6
  118. package/src/index.ts +3 -1
  119. package/src/interface.ts +35 -0
  120. package/src/main.ts +213 -194
  121. package/src/migrate.ts +30 -6
  122. package/src/migrator/access.ts +49 -32
  123. package/src/migrator/call.ts +246 -203
  124. package/src/migrator/concat.ts +15 -2
  125. package/src/migrator/interface.ts +3 -1
  126. package/src/migrator/node.ts +1 -1
  127. package/src/migrator/operator.ts +47 -33
  128. package/src/migrator/state.ts +2 -2
  129. package/src/migrator/symbol.ts +13 -0
  130. package/src/migrator/to-type.ts +22 -3
  131. package/src/migrator/utils.ts +29 -0
  132. package/src/parser.ts +27 -8
  133. package/src/re-exports.ts +28 -0
  134. package/src/scope.ts +75 -61
  135. package/src/type.ts +32 -11
  136. package/tests/analyze.ts +40 -6
  137. package/tests/compile.ts +65 -0
  138. package/tests/condition.ts +13 -5
  139. package/tests/definition.ts +205 -18
  140. package/tests/eval-complex.ts +7 -10
  141. package/tests/eval.ts +59 -12
  142. package/tests/import.ts +18 -4
  143. package/tests/main.ts +9 -0
  144. package/tests/migrate.ts +202 -0
  145. package/tests/scope.ts +3 -3
  146. package/tests/template.ts +36 -0
  147. package/dist/context.d.ts +0 -41
  148. package/dist/context.d.ts.map +0 -1
  149. package/dist/context.js +0 -18
  150. package/dist/context.js.map +0 -1
  151. package/jest.config.js +0 -3
  152. package/src/context.ts +0 -54
  153. package/tests/tsconfig.json +0 -3
  154. package/tsconfig.json +0 -3
@@ -8,7 +8,7 @@ import {
8
8
  isOperatorNode,
9
9
  isNode,
10
10
  } from 'mathjs';
11
- import { symbolName, constantValue, equalText, scalar } from './utils.js';
11
+ import { symbolName, constantValue, equalText, scalar, globalFnName } from './utils.js';
12
12
  import type { State } from './state.js';
13
13
  import type { Options, Result } from './interface.js';
14
14
  import { migrateAtomic, migrateExpr } from './node.js';
@@ -19,50 +19,58 @@ import { operations, serialize } from '@mirascript/mirascript/subtle';
19
19
  const BINARY_MATH_OPERATORS = {
20
20
  add: [
21
21
  ' + ',
22
- (l, r) => ({
22
+ (state, l, r) => ({
23
23
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
24
- code: `matrix.add(${l.code}, ${r.code})`,
24
+ code: `${globalFnName(state, 'matrix')}.add(${l.code}, ${r.code})`,
25
25
  }),
26
26
  ],
27
27
  subtract: [
28
28
  ' - ',
29
- (l, r) => ({
29
+ (state, l, r) => ({
30
30
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
31
- code: `matrix.subtract(${l.code}, ${r.code})`,
31
+ code: `${globalFnName(state, 'matrix')}.subtract(${l.code}, ${r.code})`,
32
32
  }),
33
33
  ],
34
34
  multiply: [
35
35
  ' * ',
36
- (l, r) => ({
36
+ (state, l, r) => ({
37
37
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
38
- code: `matrix.multiply(${l.code}, ${r.code})`,
38
+ code: `${globalFnName(state, 'matrix')}.multiply(${l.code}, ${r.code})`,
39
39
  }),
40
40
  ],
41
41
  dotMultiply: [
42
42
  ' * ',
43
- (l, r) => ({
43
+ (state, l, r) => ({
44
44
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
45
- code: `matrix.entrywise_multiply(${l.code}, ${r.code})`,
45
+ code: `${globalFnName(state, 'matrix')}.entrywise_multiply(${l.code}, ${r.code})`,
46
46
  }),
47
47
  ],
48
48
  divide: [
49
49
  ' / ',
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
- }),
50
+ (state, l, r) => {
51
+ if (r.type === 'array' || !r.type) {
52
+ return {
53
+ type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
54
+ code: `${globalFnName(state, 'matrix')}.multiply(${l.code}, ${globalFnName(state, 'matrix')}.invert(${r.code}))`,
55
+ };
56
+ }
57
+ return {
58
+ type: l.type === 'array' ? 'array' : l.type ? 'number' : undefined,
59
+ code: `${globalFnName(state, 'matrix')}.entrywise_divide(${l.code}, ${r.code})`,
60
+ };
61
+ },
54
62
  ],
55
63
  dotDivide: [
56
64
  ' / ',
57
- (l, r) => ({
65
+ (state, l, r) => ({
58
66
  type: l.type === 'array' || r.type === 'array' ? 'array' : l.type && r.type ? 'number' : undefined,
59
- code: `matrix.entrywise_divide(${l.code}, ${r.code})`,
67
+ code: `${globalFnName(state, 'matrix')}.entrywise_divide(${l.code}, ${r.code})`,
60
68
  }),
61
69
  ],
62
70
  mod: [' % '],
63
71
  pow: ['^', false],
64
72
  dotPow: ['^'],
65
- } satisfies Record<string, [op: string, alt?: ((l: Result, r: Result) => Result) | false]>;
73
+ } satisfies Record<string, [op: string, alt?: ((state: State, l: Result, r: Result) => Result) | false]>;
66
74
 
67
75
  const MATH_FUNCTIONS = {
68
76
  factorial: 'factorial',
@@ -99,47 +107,47 @@ function binary(
99
107
  state: State,
100
108
  l: Result,
101
109
  r: Result,
102
- op: (l: Result, r: Result) => Result,
103
- alt?: (l: Result, r: Result) => Result,
110
+ op: (state: State, l: Result, r: Result) => Result,
111
+ alt?: (state: State, l: Result, r: Result) => Result,
104
112
  ): Result {
105
113
  if (l.type && r.type && l.type !== 'array' && r.type !== 'array') {
106
- return op(l, r);
114
+ return op(state, l, r);
107
115
  }
108
116
  if (Array.isArray(l.literal) && l.literal.every((e) => !Array.isArray(e))) {
109
117
  return {
110
118
  type: 'array',
111
- code: `${l.code}::map(fn { ${op({ code: 'it' }, r).code} })`,
119
+ code: `${l.code}::${globalFnName(state, 'map')}(fn { ${op(state, { code: 'it' }, r).code} })`,
112
120
  };
113
121
  }
114
122
  if (Array.isArray(r.literal) && r.literal.every((e) => !Array.isArray(e))) {
115
123
  return {
116
124
  type: 'array',
117
- code: `${r.code}::map(fn { ${op(l, { code: 'it' }).code} })`,
125
+ code: `${r.code}::${globalFnName(state, 'map')}(fn { ${op(state, l, { code: 'it' }).code} })`,
118
126
  };
119
127
  }
120
128
  if (alt) {
121
- return alt(l, r);
129
+ return alt(state, l, r);
122
130
  }
123
131
  return {
124
132
  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} })`,
133
+ code: `${globalFnName(state, 'matrix')}.entrywise(${l.code}, ${r.code}, fn (a, b) { ${op(state, { code: 'a' }, { code: 'b' }).code} })`,
126
134
  };
127
135
  }
128
136
 
129
137
  /** 一元操作 */
130
- function unary(state: State, v: Result, op: (v: Result) => Result): Result {
138
+ function unary(state: State, v: Result, op: (state: State, v: Result) => Result): Result {
131
139
  if (v.type && v.type !== 'array') {
132
- return op(v);
140
+ return op(state, v);
133
141
  }
134
142
  if (Array.isArray(v.literal) && v.literal.every((e) => !Array.isArray(e))) {
135
143
  return {
136
144
  type: 'array',
137
- code: `${v.code}::map(fn { ${op({ code: 'it' }).code} })`,
145
+ code: `${v.code}::${globalFnName(state, 'map')}(fn { ${op(state, { code: 'it' }).code} })`,
138
146
  };
139
147
  }
140
148
  return {
141
149
  type: 'array',
142
- code: `matrix.entrywise(${v.code}, nil, fn { ${op({ code: 'it' }).code} })`,
150
+ code: `${globalFnName(state, 'matrix')}.entrywise(${v.code}, nil, fn { ${op(state, { code: 'it' }).code} })`,
143
151
  };
144
152
  }
145
153
 
@@ -171,14 +179,14 @@ export function migrateOperator(
171
179
  state,
172
180
  migrateExpr(state, a0),
173
181
  migrateExpr(state, a1),
174
- (l, r) => ({
182
+ (state, l, r) => ({
175
183
  type: 'number',
176
184
  code: `${l.code}${op}${r.code}`,
177
185
  }),
178
186
  alt === false
179
- ? (l, r) => {
187
+ ? (state, l, r) => {
180
188
  state.warn(`'${op.trim()}' 不支持矩阵,计算结果可能不一致`);
181
- return { code: `${l.code} ^ ${r.code}` };
189
+ return { code: `${l.code}${op}${r.code}` };
182
190
  }
183
191
  : alt,
184
192
  );
@@ -187,9 +195,9 @@ export function migrateOperator(
187
195
  case 'unaryMinus':
188
196
  case 'unaryPlus': {
189
197
  const op = fn === 'unaryMinus' ? '-' : '+';
190
- const f = fn === 'unaryMinus' ? operations.$Neg : operations.$Pos;
191
198
  const exp = migrateExpr(state, a0);
192
199
  if (typeof exp.literal == 'number') {
200
+ const f = fn === 'unaryMinus' ? operations.$Neg : operations.$Pos;
193
201
  const v = f(exp.literal);
194
202
  return {
195
203
  type: 'number',
@@ -197,7 +205,7 @@ export function migrateOperator(
197
205
  code: serialize(v),
198
206
  };
199
207
  }
200
- return unary(state, migrateExpr(state, a0), (v) => ({
208
+ return unary(state, migrateExpr(state, a0), (state, v) => ({
201
209
  type: 'number',
202
210
  code: `${op}${v.code}`,
203
211
  }));
@@ -307,7 +315,13 @@ export function migrateOperator(
307
315
  return r;
308
316
  };
309
317
 
310
- return binary(state, a(a0), a(a1), (l, r) => {
318
+ return binary(state, a(a0), a(a1), (state, l, r) => {
319
+ if (l.type === 'boolean' && r.type === 'boolean') {
320
+ return {
321
+ type: 'boolean',
322
+ code: `${l.code} ${op} ${r.code}`,
323
+ };
324
+ }
311
325
  return {
312
326
  type: 'boolean',
313
327
  code: `${toNumber(state, l).code} ${op} ${toNumber(state, r).code}`,
@@ -1,4 +1,4 @@
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,
@@ -87,7 +87,7 @@ export class BaseState {
87
87
  private readonly expr: string,
88
88
  readonly condition: boolean,
89
89
  /** 可识别的全局环境 */
90
- readonly globals: VmContext = createVmContext(),
90
+ readonly globals: VmContext,
91
91
  ) {}
92
92
  /** 帮助函数 */
93
93
  private readonly helpers = new Set<string>();
@@ -18,6 +18,7 @@ export function migrateSymbol(state: State, node: SymbolNode, migrateConst: bool
18
18
  return {
19
19
  type: operations.$Type(global),
20
20
  code: name,
21
+ global,
21
22
  };
22
23
  }
23
24
  if (name.startsWith('$') || name.startsWith('@')) {
@@ -45,6 +46,18 @@ export function migrateSymbol(state: State, node: SymbolNode, migrateConst: bool
45
46
  code: `@e`,
46
47
  };
47
48
  }
49
+ if (name === '__PREVIEW') {
50
+ return {
51
+ type: 'boolean',
52
+ code: '@PREVIEW',
53
+ };
54
+ }
55
+ if (name === '__ICON') {
56
+ return {
57
+ type: 'boolean',
58
+ code: '@ICON',
59
+ };
60
+ }
48
61
  }
49
62
  state.warn(`符号 '${name}' 未定义`);
50
63
  return {
@@ -3,11 +3,14 @@ import type { Result } from './interface.js';
3
3
  import { serialize } from '@mirascript/mirascript/subtle';
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("fn @@to_boolean { it != '' && it != 0 && it is not nan && !!it }");
11
+ state.helper(
12
+ "fn @@to_boolean { if it is nil { return nil; } it != '' && it != '0' && it != 0 && it is not nan && !!it }",
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: `to_string(${node.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.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: `to_number(${node.code})`,
98
+ code: `${globalFnName(state, 'to_number')}(${node.code})`,
80
99
  };
81
100
  }
82
101
  if (isConstantNode(node)) {
@@ -4,6 +4,14 @@ import type { State } from './state.js';
4
4
  import { toString } from './to-type.js';
5
5
  import { migrateExpr } from './node.js';
6
6
 
7
+ /** 获取全局函数的名字 */
8
+ export function globalFnName(state: State, fnName: string): string {
9
+ if (state.locals.has(fnName)) {
10
+ return `global.${fnName}`;
11
+ }
12
+ return fnName;
13
+ }
14
+
7
15
  /** 获取 symbol name */
8
16
  export function symbolName(node: MathNode): string | undefined {
9
17
  if (!isSymbolNode(node)) return undefined;
@@ -65,3 +73,24 @@ export function equalText(state: State, op: '==' | '!=', l: MathNode, r: MathNod
65
73
  code: `${toString(state, lr).code} ${op} ${toString(state, rr).code}`,
66
74
  };
67
75
  }
76
+
77
+ /** 计算长度 */
78
+ export function len(state: State, obj: Result): Result {
79
+ if (obj.type === 'string')
80
+ return {
81
+ type: 'number',
82
+ code: `len(chars(${obj.code}))`,
83
+ };
84
+ if (obj.type === 'array')
85
+ return {
86
+ type: 'number',
87
+ code: `len(${obj.code})`,
88
+ };
89
+ state.helper(
90
+ `fn @@length(x) { if type(x) == 'string' { len(chars(x)) } else if type(x) == 'array' { len(x) } else { x.length } }`,
91
+ );
92
+ return {
93
+ type: 'number',
94
+ code: `@@length(${obj.code})`,
95
+ };
96
+ }
package/src/parser.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { compileSync, type VmScript } from '@mirascript/mirascript';
2
- import type { Context } from './context.js';
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,36 @@ 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, template: boolean): LRUCache<string, EvalExpressionCache> {
61
+ const key = template ? kTemplateCache : kExpressionCache;
62
+ if (!(key in evaluator)) {
63
+ const size = template ? evaluator.options.templateCacheSize : evaluator.options.expressionCacheSize;
64
+ Object.defineProperty(evaluator, key, {
65
+ value: new LRUCache<string, EvalExpressionCache>({ max: size! || 50 }),
66
+ });
67
+ }
68
+ return (evaluator as Evaluator & Record<typeof key, LRUCache<string, EvalExpressionCache>>)[key];
69
+ }
70
+
54
71
  /**
55
72
  * 解析表达式
56
73
  */
57
- export function parse(context: Context, expression: string, throws: boolean): ExpressionCache {
74
+ export function parse(evaluator: Evaluator, expression: string, throws: boolean, template: boolean): ExpressionCache {
58
75
  if (typeof expression != 'string') {
59
76
  throw new TypeError(`${String(expression)} is not a valid expression`);
60
77
  }
61
- const v = context.expressionCache.get(expression);
62
- if (v) return v;
63
- const complied = compile(expression);
78
+ const cache = getCache(evaluator, template);
79
+ const cached = cache.get(expression);
80
+ if (cached != null) return cached;
81
+ const complied = compile(expression, template);
64
82
  if (complied.error) {
65
83
  if (throws) throw complied.error;
84
+ } else {
85
+ cache.set(expression, complied);
66
86
  }
67
- context.expressionCache.set(expression, complied);
68
87
  return complied;
69
88
  }
@@ -0,0 +1,28 @@
1
+ export {
2
+ isVmAny,
3
+ isVmArray,
4
+ isVmConst,
5
+ isVmContext,
6
+ isVmExtern,
7
+ isVmFunction,
8
+ isVmImmutable,
9
+ isVmModule,
10
+ isVmPrimitive,
11
+ isVmRecord,
12
+ isVmValue,
13
+ VmExtern,
14
+ VmFunction,
15
+ VmModule,
16
+ } from '@mirascript/mirascript';
17
+ export { serialize, serializePropName, serializeString, lib, operations } from '@mirascript/mirascript/subtle';
18
+ export type {
19
+ VmAny,
20
+ VmArray,
21
+ VmConst,
22
+ VmContext,
23
+ VmImmutable,
24
+ VmPrimitive,
25
+ VmRecord,
26
+ VmUninitialized,
27
+ VmValue,
28
+ } from '@mirascript/mirascript';
package/src/scope.ts CHANGED
@@ -1,5 +1,13 @@
1
- import { createVmContext, isVmExtern, type VmAny, type VmContext, type VmValue } from '@mirascript/mirascript';
2
- import type { Context } from './context.js';
1
+ import {
2
+ createVmContext,
3
+ isVmExtern,
4
+ type VmAny,
5
+ type VmArray,
6
+ type VmContext,
7
+ type VmRecord,
8
+ type VmValue,
9
+ isVmModule,
10
+ } from '@mirascript/mirascript';
3
11
  import {
4
12
  type CompiledExpression,
5
13
  type Expression,
@@ -7,16 +15,27 @@ import {
7
15
  ExpressionTag,
8
16
  isExpression,
9
17
  } from './expression.js';
10
- import { DEFAULTS, evaluateEval } from './eval.js';
18
+ import { evaluateEval } from './eval.js';
11
19
  import { parse } from './parser.js';
12
20
  import { TypeInfo } from './type.js';
21
+ import type { Evaluator } from './main.js';
13
22
  const { hasOwn } = Object;
14
23
 
15
24
  const RAW = Symbol.for('@private/expression:raw');
16
25
 
17
26
  /** 是否需要创建代理对象 */
18
- function needsProxy(value: unknown): value is Expression | object {
19
- return isExpression(value) || (value != null && typeof value == 'object');
27
+ function needsProxy(value: ExpressionOrValue | undefined): value is Expression | VmArray | VmRecord {
28
+ // 先检查 isExpression,因为 'function' 也有可能是 Expression
29
+ if (isExpression(value)) {
30
+ return true;
31
+ }
32
+ if (value == null || typeof value != 'object') {
33
+ return false;
34
+ }
35
+ if (isVmExtern(value) || isVmModule(value)) {
36
+ return false;
37
+ }
38
+ return true;
20
39
  }
21
40
 
22
41
  /** 创建代理对象 */
@@ -53,13 +72,10 @@ class ScopeProxyHandler implements ProxyHandler<object> {
53
72
  if (p === RAW) {
54
73
  return target;
55
74
  }
56
- const value = Reflect.get(target, p) as unknown;
57
- if (!needsProxy(value)) {
75
+ const value = Reflect.get(target, p) as ExpressionOrValue;
76
+ if (isVmExtern(target) || !needsProxy(value)) {
58
77
  return value;
59
78
  }
60
- if (isVmExtern(target)) {
61
- return createProxy(value, this.scope, this.path);
62
- }
63
79
  return createProxy(value, this.scope, [...this.path, p]);
64
80
  }
65
81
  }
@@ -70,58 +86,61 @@ export class Scope {
70
86
  static readonly MAX_RECURSION = 500;
71
87
  constructor(
72
88
  /** 生成环境的工厂函数 */
73
- readonly scope?:
89
+ readonly scope:
74
90
  | ((key: string) => ExpressionOrValue | undefined)
75
- | Record<string, ExpressionOrValue | undefined>,
91
+ | Record<string, ExpressionOrValue | undefined>
92
+ | null = null,
76
93
  /** 求值失败是否抛异常 */
77
94
  readonly throws = true,
78
95
  /** 执行环境名称 */
79
96
  readonly name = '<anonymous>',
80
- ) {
81
- if (scope == null) {
82
- this.getter = (key) => this.context?.imported.get(key);
83
- } else if (typeof scope == 'function') {
84
- this.getter = (key) => {
85
- const v = scope(key);
86
- if (v === undefined) return this.context?.imported.get(key);
87
- if (!needsProxy(v)) return v;
88
- return createProxy(v, this, [key]) as VmValue;
89
- };
90
- } else {
91
- this.getter = (key) => {
92
- if (!hasOwn(scope, key)) return this.context?.imported.get(key);
93
- const v = scope[key];
94
- if (!needsProxy(v)) return v;
95
- return createProxy(v, this, [key]) as VmValue;
96
- };
97
- }
98
- }
97
+ ) {}
99
98
 
100
- protected readonly getter: (key: string) => VmAny;
101
- /** 上下文 */
102
- protected context: Context | null = null;
99
+ /** 执行器上下文 */
100
+ protected evaluator: Evaluator | null = null;
103
101
  /** 求值计数 */
104
102
  protected evalCounter = 0;
105
- /** 重置执行环境 */
106
- reset(context: Context | null): () => void {
107
- const oldContext = this.context;
103
+ /** 重置执行器 */
104
+ reset(evaluator: Evaluator | null): () => void {
105
+ const oldEvaluator = this.evaluator;
108
106
  const oldCounter = this.evalCounter;
109
- this.context = context;
107
+ this.evaluator = evaluator;
110
108
  this.evalCounter = 0;
111
109
  return () => {
112
- this.context = oldContext;
110
+ this.evaluator = oldEvaluator;
113
111
  this.evalCounter = oldCounter;
114
112
  };
115
113
  }
116
114
  /** 函数的执行环境,惰性求值 */
117
- protected _proxy?: VmContext;
115
+ protected _proxy: VmContext | null = null;
118
116
  /** 函数的执行环境,惰性求值 */
119
117
  get proxy(): VmContext {
120
- this._proxy ??= createVmContext(this.getter);
121
- return this._proxy;
118
+ if (this._proxy != null) return this._proxy;
119
+ let getter: (key: string) => VmAny;
120
+ const { scope } = this;
121
+ if (scope == null) {
122
+ getter = (key) => this.evaluator?.imported.get(key);
123
+ } else if (typeof scope == 'function') {
124
+ getter = (key) => {
125
+ const v = scope(key);
126
+ if (v === undefined) return this.evaluator?.imported.get(key);
127
+ if (!needsProxy(v)) return v;
128
+ return createProxy(v, this, [key]) as VmValue;
129
+ };
130
+ } else {
131
+ getter = (key) => {
132
+ if (!hasOwn(scope, key)) return this.evaluator?.imported.get(key);
133
+ const v = scope[key];
134
+ if (!needsProxy(v)) return v;
135
+ return createProxy(v, this, [key]) as VmValue;
136
+ };
137
+ }
138
+ const proxy = createVmContext(getter);
139
+ this._proxy = proxy;
140
+ return proxy;
122
141
  }
123
142
 
124
- /** 检查环境中是否有值 */
143
+ /** 检查环境中是否有值,不包含 evaluator 和 default context 中的值 */
125
144
  has(key: string): boolean {
126
145
  const { scope } = this;
127
146
  if (scope == null) return false;
@@ -134,8 +153,8 @@ export class Scope {
134
153
  return v !== undefined;
135
154
  }
136
155
 
137
- /** 获取环境中的值 */
138
- get(key: string): VmAny {
156
+ /** 获取环境中的值,不包含 evaluator 和 default context 中的值 */
157
+ get<V extends VmValue>(key: string): V | undefined {
139
158
  const { scope } = this;
140
159
  if (scope == null) return undefined;
141
160
  let v;
@@ -144,13 +163,11 @@ export class Scope {
144
163
  } else if (hasOwn(scope, key)) {
145
164
  v = scope[key];
146
165
  }
147
- if (!isExpression(v)) return v;
148
- return this.eval(v, [key]);
166
+ if (!isExpression(v)) return v satisfies VmValue | undefined as V | undefined;
167
+ return this.eval(v, [key]) satisfies VmValue as V;
149
168
  }
150
169
 
151
- /**
152
- * 表达式递归求值
153
- */
170
+ /** 表达式递归求值 */
154
171
  eval(expression: Expression | CompiledExpression, path: readonly PropertyKey[]): VmValue {
155
172
  if (this.evalCounter >= Scope.MAX_RECURSION) {
156
173
  throw new Error(`Execution recursion exceeds limit`);
@@ -158,27 +175,24 @@ export class Scope {
158
175
 
159
176
  this.evalCounter++;
160
177
  try {
161
- let result;
178
+ let result: VmValue | null = null;
162
179
  if (typeof expression == 'function') {
163
- result = expression(this) as VmValue;
180
+ result = expression(this, this.evaluator!);
164
181
  } else {
165
- const { context } = this;
166
- if (!context) {
167
- throw new Error(`Undefined context`);
182
+ const { evaluator } = this;
183
+ if (!evaluator) {
184
+ throw new Error(`Undefined evaluator`);
168
185
  }
169
- const exp = parse(context, expression.source, this.throws);
186
+ const exp = parse(evaluator, expression.source, this.throws, false);
170
187
  if (exp.func != null) {
171
- result = evaluateEval(context, exp, this);
188
+ result = evaluateEval(evaluator, exp, this);
172
189
  } else if (exp.error != null) {
173
190
  throw exp.error;
174
- } else {
175
- result = DEFAULTS;
176
191
  }
177
192
  }
178
- if (result === DEFAULTS) return null;
179
193
  const tag = expression[ExpressionTag];
180
194
  if (!tag) return result;
181
- return TypeInfo.to(result, TypeInfo.parse(tag));
195
+ return TypeInfo.to(result, tag);
182
196
  } catch (ex) {
183
197
  throw new Error(`${(ex as Error).message || String(ex)}\nIn ${this.name}: @ ${path.join('/')}`);
184
198
  }