@codehz/json-expr 0.6.2 → 0.6.4
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/index.mjs +601 -315
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -63,8 +63,8 @@ function createGenerateContext() {
|
|
|
63
63
|
/**
|
|
64
64
|
* 从 AST 生成规范化的代码
|
|
65
65
|
*/
|
|
66
|
-
function generate(node) {
|
|
67
|
-
return generateWithContext(node, createGenerateContext());
|
|
66
|
+
function generate(node, options = {}) {
|
|
67
|
+
return generateWithContext(node, createGenerateContext(), options);
|
|
68
68
|
}
|
|
69
69
|
/** 需要空格分隔的关键字运算符 */
|
|
70
70
|
const KEYWORD_BINARY_OPERATORS = new Set(["in", "instanceof"]);
|
|
@@ -72,46 +72,47 @@ const KEYWORD_UNARY_OPERATORS = new Set(["typeof", "void"]);
|
|
|
72
72
|
/**
|
|
73
73
|
* 带上下文的代码生成
|
|
74
74
|
*/
|
|
75
|
-
function generateWithContext(node, ctx) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
case "
|
|
79
|
-
case "
|
|
75
|
+
function generateWithContext(node, ctx, options = {}) {
|
|
76
|
+
const rewritten = options.rewriteNode?.(node, ctx) ?? node;
|
|
77
|
+
switch (rewritten.type) {
|
|
78
|
+
case "NumberLiteral": return rewritten.raw;
|
|
79
|
+
case "StringLiteral": return JSON.stringify(rewritten.value);
|
|
80
|
+
case "BooleanLiteral": return rewritten.value ? "true" : "false";
|
|
80
81
|
case "NullLiteral": return "null";
|
|
81
|
-
case "Identifier": return
|
|
82
|
-
case "Placeholder": return ctx.paramMapping.get(
|
|
82
|
+
case "Identifier": return rewritten.name;
|
|
83
|
+
case "Placeholder": return ctx.paramMapping.get(rewritten.id) ?? `$$${rewritten.id.description}$$`;
|
|
83
84
|
case "BinaryExpr": {
|
|
84
|
-
const left = wrapIfNeededWithContext(
|
|
85
|
-
const right = wrapIfNeededWithContext(
|
|
86
|
-
const sep = KEYWORD_BINARY_OPERATORS.has(
|
|
87
|
-
return `${left}${sep}${
|
|
85
|
+
const left = wrapIfNeededWithContext(rewritten.left, rewritten, "left", ctx, options);
|
|
86
|
+
const right = wrapIfNeededWithContext(rewritten.right, rewritten, "right", ctx, options);
|
|
87
|
+
const sep = KEYWORD_BINARY_OPERATORS.has(rewritten.operator) ? " " : "";
|
|
88
|
+
return `${left}${sep}${rewritten.operator}${sep}${right}`;
|
|
88
89
|
}
|
|
89
90
|
case "UnaryExpr": {
|
|
90
|
-
if (!
|
|
91
|
-
const arg = wrapIfNeededWithContext(
|
|
92
|
-
const sep = KEYWORD_UNARY_OPERATORS.has(
|
|
93
|
-
return `${
|
|
91
|
+
if (!rewritten.prefix) return generateWithContext(rewritten.argument, ctx, options) + rewritten.operator;
|
|
92
|
+
const arg = wrapIfNeededWithContext(rewritten.argument, rewritten, "argument", ctx, options);
|
|
93
|
+
const sep = KEYWORD_UNARY_OPERATORS.has(rewritten.operator) ? " " : "";
|
|
94
|
+
return `${rewritten.operator}${sep}${arg}`;
|
|
94
95
|
}
|
|
95
|
-
case "ConditionalExpr": return `${wrapIfNeededWithContext(
|
|
96
|
+
case "ConditionalExpr": return `${wrapIfNeededWithContext(rewritten.test, rewritten, "test", ctx, options)}?${wrapIfNeededWithContext(rewritten.consequent, rewritten, "consequent", ctx, options)}:${wrapIfNeededWithContext(rewritten.alternate, rewritten, "alternate", ctx, options)}`;
|
|
96
97
|
case "MemberExpr": {
|
|
97
|
-
const object = wrapIfNeededWithContext(
|
|
98
|
-
const property = generateWithContext(
|
|
99
|
-
const accessor =
|
|
100
|
-
return
|
|
98
|
+
const object = wrapIfNeededWithContext(rewritten.object, rewritten, "object", ctx, options);
|
|
99
|
+
const property = generateWithContext(rewritten.property, ctx, options);
|
|
100
|
+
const accessor = rewritten.optional ? "?." : rewritten.computed ? "" : ".";
|
|
101
|
+
return rewritten.computed ? `${object}${accessor}[${property}]` : `${object}${accessor}${property}`;
|
|
101
102
|
}
|
|
102
103
|
case "CallExpr": {
|
|
103
|
-
const callee = wrapIfNeededWithContext(
|
|
104
|
-
const args =
|
|
105
|
-
return `${
|
|
106
|
-
}
|
|
107
|
-
case "ArrayExpr": return `[${
|
|
108
|
-
case "ObjectExpr": return `{${
|
|
109
|
-
if (prop.shorthand) return generateWithContext(prop.key, ctx);
|
|
110
|
-
return `${prop.computed ? `[${generateWithContext(prop.key, ctx)}]` : generateWithContext(prop.key, ctx)}:${generateWithContext(prop.value, ctx)}`;
|
|
104
|
+
const callee = wrapIfNeededWithContext(rewritten.callee, rewritten, "callee", ctx, options);
|
|
105
|
+
const args = rewritten.arguments.map((arg) => generateWithContext(arg, ctx, options)).join(",");
|
|
106
|
+
return `${rewritten.callee.type === "Identifier" && BUILTIN_CONSTRUCTORS.has(rewritten.callee.name) ? "new " : ""}${callee}${rewritten.optional ? "?." : ""}(${args})`;
|
|
107
|
+
}
|
|
108
|
+
case "ArrayExpr": return `[${rewritten.elements.map((el) => generateWithContext(el, ctx, options)).join(",")}]`;
|
|
109
|
+
case "ObjectExpr": return `{${rewritten.properties.map((prop) => {
|
|
110
|
+
if (prop.shorthand) return generateWithContext(prop.key, ctx, options);
|
|
111
|
+
return `${prop.computed ? `[${generateWithContext(prop.key, ctx, options)}]` : generateWithContext(prop.key, ctx, options)}:${generateWithContext(prop.value, ctx, options)}`;
|
|
111
112
|
}).join(",")}}`;
|
|
112
|
-
case "ArrowFunctionExpr": return generateArrowFunction(
|
|
113
|
+
case "ArrowFunctionExpr": return generateArrowFunction(rewritten, ctx, options);
|
|
113
114
|
default: {
|
|
114
|
-
const unknownNode =
|
|
115
|
+
const unknownNode = rewritten;
|
|
115
116
|
throw new Error(`Unknown node type: ${unknownNode.type ?? "unknown"}`);
|
|
116
117
|
}
|
|
117
118
|
}
|
|
@@ -120,7 +121,7 @@ function generateWithContext(node, ctx) {
|
|
|
120
121
|
* 生成箭头函数代码
|
|
121
122
|
* 为 Placeholder 参数分配唯一名称,避免嵌套 lambda 冲突
|
|
122
123
|
*/
|
|
123
|
-
function generateArrowFunction(node, ctx) {
|
|
124
|
+
function generateArrowFunction(node, ctx, options) {
|
|
124
125
|
const allocatedParams = [];
|
|
125
126
|
const paramNames = [];
|
|
126
127
|
for (const param of node.params) if (param.type === "Identifier") paramNames.push(param.name);
|
|
@@ -135,7 +136,7 @@ function generateArrowFunction(node, ctx) {
|
|
|
135
136
|
ctx.paramMapping.set(param.id, name);
|
|
136
137
|
}
|
|
137
138
|
const paramsStr = paramNames.length === 1 ? paramNames[0] : `(${paramNames.join(",")})`;
|
|
138
|
-
const bodyStr = node.body.type === "ObjectExpr" ? `(${generateWithContext(node.body, ctx)})` : generateWithContext(node.body, ctx);
|
|
139
|
+
const bodyStr = node.body.type === "ObjectExpr" ? `(${generateWithContext(node.body, ctx, options)})` : generateWithContext(node.body, ctx, options);
|
|
139
140
|
for (const { id, name } of allocatedParams) {
|
|
140
141
|
ctx.paramMapping.delete(id);
|
|
141
142
|
ctx.usedParamNames.delete(name);
|
|
@@ -153,9 +154,10 @@ function allocateUniqueName(usedNames) {
|
|
|
153
154
|
/**
|
|
154
155
|
* 判断是否需要括号包裹,并生成代码(带上下文版本)
|
|
155
156
|
*/
|
|
156
|
-
function wrapIfNeededWithContext(child, parent, position, ctx) {
|
|
157
|
-
const
|
|
158
|
-
|
|
157
|
+
function wrapIfNeededWithContext(child, parent, position, ctx, options = {}) {
|
|
158
|
+
const rewrittenChild = options.rewriteNode?.(child, ctx) ?? child;
|
|
159
|
+
const code = generateWithContext(rewrittenChild, ctx, options);
|
|
160
|
+
if (needsParens(rewrittenChild, parent, position)) return `(${code})`;
|
|
159
161
|
return code;
|
|
160
162
|
}
|
|
161
163
|
/**
|
|
@@ -199,169 +201,257 @@ function transformIdentifiers(node, transform) {
|
|
|
199
201
|
switch (node.type) {
|
|
200
202
|
case "Identifier": {
|
|
201
203
|
const result = transform(node.name);
|
|
202
|
-
|
|
204
|
+
if (typeof result !== "string") return result;
|
|
205
|
+
return result === node.name ? node : {
|
|
203
206
|
...node,
|
|
204
207
|
name: result
|
|
205
|
-
}
|
|
208
|
+
};
|
|
206
209
|
}
|
|
207
210
|
case "Placeholder": return node;
|
|
208
|
-
case "BinaryExpr":
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
right
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
case "
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
211
|
+
case "BinaryExpr": {
|
|
212
|
+
const left = transformIdentifiers(node.left, transform);
|
|
213
|
+
const right = transformIdentifiers(node.right, transform);
|
|
214
|
+
return left === node.left && right === node.right ? node : {
|
|
215
|
+
...node,
|
|
216
|
+
left,
|
|
217
|
+
right
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
case "UnaryExpr": {
|
|
221
|
+
const argument = transformIdentifiers(node.argument, transform);
|
|
222
|
+
return argument === node.argument ? node : {
|
|
223
|
+
...node,
|
|
224
|
+
argument
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
case "ConditionalExpr": {
|
|
228
|
+
const test = transformIdentifiers(node.test, transform);
|
|
229
|
+
const consequent = transformIdentifiers(node.consequent, transform);
|
|
230
|
+
const alternate = transformIdentifiers(node.alternate, transform);
|
|
231
|
+
return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
|
|
232
|
+
...node,
|
|
233
|
+
test,
|
|
234
|
+
consequent,
|
|
235
|
+
alternate
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
case "MemberExpr": {
|
|
239
|
+
const object = transformIdentifiers(node.object, transform);
|
|
240
|
+
const property = node.computed ? transformIdentifiers(node.property, transform) : node.property;
|
|
241
|
+
return object === node.object && property === node.property ? node : {
|
|
242
|
+
...node,
|
|
243
|
+
object,
|
|
244
|
+
property
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
case "CallExpr": {
|
|
248
|
+
const callee = transformIdentifiers(node.callee, transform);
|
|
249
|
+
const arguments_ = mapAstNodes$1(node.arguments, (arg) => transformIdentifiers(arg, transform));
|
|
250
|
+
return callee === node.callee && arguments_ === node.arguments ? node : {
|
|
251
|
+
...node,
|
|
252
|
+
callee,
|
|
253
|
+
arguments: arguments_
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
case "ArrayExpr": {
|
|
257
|
+
const elements = mapAstNodes$1(node.elements, (el) => transformIdentifiers(el, transform));
|
|
258
|
+
return elements === node.elements ? node : {
|
|
259
|
+
...node,
|
|
260
|
+
elements
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
case "ObjectExpr": {
|
|
264
|
+
const properties = mapObjectProperties$1(node.properties, (prop) => {
|
|
265
|
+
const key = prop.computed ? transformIdentifiers(prop.key, transform) : prop.key;
|
|
266
|
+
const value = transformIdentifiers(prop.value, transform);
|
|
267
|
+
return key === prop.key && value === prop.value ? prop : {
|
|
268
|
+
...prop,
|
|
269
|
+
key,
|
|
270
|
+
value
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
return properties === node.properties ? node : {
|
|
274
|
+
...node,
|
|
275
|
+
properties
|
|
276
|
+
};
|
|
277
|
+
}
|
|
245
278
|
case "ArrowFunctionExpr": {
|
|
246
279
|
const paramNames = new Set(node.params.filter((p) => p.type === "Identifier").map((p) => p.name));
|
|
247
|
-
|
|
280
|
+
const body = transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name));
|
|
281
|
+
return body === node.body ? node : {
|
|
248
282
|
...node,
|
|
249
|
-
body
|
|
283
|
+
body
|
|
250
284
|
};
|
|
251
285
|
}
|
|
252
286
|
default: return node;
|
|
253
287
|
}
|
|
254
288
|
}
|
|
255
289
|
/**
|
|
256
|
-
*
|
|
257
|
-
* 回调函数接收 symbol,返回 Identifier 节点的名称
|
|
258
|
-
* 如果返回 null/undefined,则保留原始 Placeholder 节点
|
|
290
|
+
* expr() 专用:将 context 中的变量替换为 Placeholder。
|
|
259
291
|
*/
|
|
260
|
-
function
|
|
292
|
+
function transformExprVariables(node, nameToId) {
|
|
293
|
+
return transformIdentifiers(node, (name) => {
|
|
294
|
+
const id = nameToId.get(name);
|
|
295
|
+
return id ? {
|
|
296
|
+
type: "Placeholder",
|
|
297
|
+
id
|
|
298
|
+
} : name;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* expr() 专用:单次遍历完成变量替换、子表达式引用计数和延迟编译收集。
|
|
303
|
+
*/
|
|
304
|
+
function transformExprIdentifiers(node, nameToId, nameToExprAST) {
|
|
305
|
+
const refCounts = /* @__PURE__ */ new Map();
|
|
306
|
+
const candidateRefs = /* @__PURE__ */ new Map();
|
|
307
|
+
const ast = transformExprIdentifiersWithScope(node, nameToId, nameToExprAST, refCounts, candidateRefs);
|
|
308
|
+
let deferredAsts;
|
|
309
|
+
for (const [name, refs] of candidateRefs) {
|
|
310
|
+
if ((refCounts.get(name) ?? 0) <= 1) {
|
|
311
|
+
replaceNode(refs[0], nameToExprAST.get(name));
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (!deferredAsts) deferredAsts = /* @__PURE__ */ new Map();
|
|
315
|
+
deferredAsts.set(name, nameToExprAST.get(name));
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
ast,
|
|
319
|
+
deferredAsts
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function mapAstNodes$1(nodes, transform) {
|
|
323
|
+
let result;
|
|
324
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
325
|
+
const node = nodes[i];
|
|
326
|
+
const transformed = transform(node);
|
|
327
|
+
if (result) {
|
|
328
|
+
result.push(transformed);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (transformed !== node) {
|
|
332
|
+
result = nodes.slice(0, i);
|
|
333
|
+
result.push(transformed);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return result ?? nodes;
|
|
337
|
+
}
|
|
338
|
+
function mapObjectProperties$1(properties, transform) {
|
|
339
|
+
let result;
|
|
340
|
+
for (let i = 0; i < properties.length; i++) {
|
|
341
|
+
const property = properties[i];
|
|
342
|
+
const transformed = transform(property);
|
|
343
|
+
if (result) {
|
|
344
|
+
result.push(transformed);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (transformed !== property) {
|
|
348
|
+
result = properties.slice(0, i);
|
|
349
|
+
result.push(transformed);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return result ?? properties;
|
|
353
|
+
}
|
|
354
|
+
function transformExprIdentifiersWithScope(node, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) {
|
|
261
355
|
switch (node.type) {
|
|
262
|
-
case "
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
356
|
+
case "Identifier": {
|
|
357
|
+
if (shadowedNames?.has(node.name)) return node;
|
|
358
|
+
const id = nameToId.get(node.name);
|
|
359
|
+
if (id) return {
|
|
360
|
+
type: "Placeholder",
|
|
361
|
+
id
|
|
362
|
+
};
|
|
363
|
+
if (!nameToExprAST.get(node.name)) return node;
|
|
364
|
+
refCounts.set(node.name, (refCounts.get(node.name) ?? 0) + 1);
|
|
365
|
+
const candidateNode = { ...node };
|
|
366
|
+
const refs = candidateRefs.get(node.name);
|
|
367
|
+
if (refs) refs.push(candidateNode);
|
|
368
|
+
else candidateRefs.set(node.name, [candidateNode]);
|
|
369
|
+
return candidateNode;
|
|
370
|
+
}
|
|
371
|
+
case "Placeholder": return node;
|
|
372
|
+
case "BinaryExpr": {
|
|
373
|
+
const left = transformExprIdentifiersWithScope(node.left, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
374
|
+
const right = transformExprIdentifiersWithScope(node.right, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
375
|
+
return left === node.left && right === node.right ? node : {
|
|
376
|
+
...node,
|
|
377
|
+
left,
|
|
378
|
+
right
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
case "UnaryExpr": {
|
|
382
|
+
const argument = transformExprIdentifiersWithScope(node.argument, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
383
|
+
return argument === node.argument ? node : {
|
|
384
|
+
...node,
|
|
385
|
+
argument
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
case "ConditionalExpr": {
|
|
389
|
+
const test = transformExprIdentifiersWithScope(node.test, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
390
|
+
const consequent = transformExprIdentifiersWithScope(node.consequent, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
391
|
+
const alternate = transformExprIdentifiersWithScope(node.alternate, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
392
|
+
return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
|
|
393
|
+
...node,
|
|
394
|
+
test,
|
|
395
|
+
consequent,
|
|
396
|
+
alternate
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
case "MemberExpr": {
|
|
400
|
+
const object = transformExprIdentifiersWithScope(node.object, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
401
|
+
const property = node.computed ? transformExprIdentifiersWithScope(node.property, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) : node.property;
|
|
402
|
+
return object === node.object && property === node.property ? node : {
|
|
403
|
+
...node,
|
|
404
|
+
object,
|
|
405
|
+
property
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
case "CallExpr": {
|
|
409
|
+
const callee = transformExprIdentifiersWithScope(node.callee, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
410
|
+
const arguments_ = mapAstNodes$1(node.arguments, (arg) => transformExprIdentifiersWithScope(arg, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames));
|
|
411
|
+
return callee === node.callee && arguments_ === node.arguments ? node : {
|
|
412
|
+
...node,
|
|
413
|
+
callee,
|
|
414
|
+
arguments: arguments_
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
case "ArrayExpr": {
|
|
418
|
+
const elements = mapAstNodes$1(node.elements, (el) => transformExprIdentifiersWithScope(el, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames));
|
|
419
|
+
return elements === node.elements ? node : {
|
|
420
|
+
...node,
|
|
421
|
+
elements
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
case "ObjectExpr": {
|
|
425
|
+
const properties = mapObjectProperties$1(node.properties, (prop) => {
|
|
426
|
+
const key = prop.computed ? transformExprIdentifiersWithScope(prop.key, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames) : prop.key;
|
|
427
|
+
const value = transformExprIdentifiersWithScope(prop.value, nameToId, nameToExprAST, refCounts, candidateRefs, shadowedNames);
|
|
428
|
+
return key === prop.key && value === prop.value ? prop : {
|
|
429
|
+
...prop,
|
|
430
|
+
key,
|
|
431
|
+
value
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
return properties === node.properties ? node : {
|
|
435
|
+
...node,
|
|
436
|
+
properties
|
|
437
|
+
};
|
|
268
438
|
}
|
|
269
|
-
case "Identifier": return node;
|
|
270
|
-
case "BinaryExpr": return {
|
|
271
|
-
...node,
|
|
272
|
-
left: transformPlaceholders(node.left, transform),
|
|
273
|
-
right: transformPlaceholders(node.right, transform)
|
|
274
|
-
};
|
|
275
|
-
case "UnaryExpr": return {
|
|
276
|
-
...node,
|
|
277
|
-
argument: transformPlaceholders(node.argument, transform)
|
|
278
|
-
};
|
|
279
|
-
case "ConditionalExpr": return {
|
|
280
|
-
...node,
|
|
281
|
-
test: transformPlaceholders(node.test, transform),
|
|
282
|
-
consequent: transformPlaceholders(node.consequent, transform),
|
|
283
|
-
alternate: transformPlaceholders(node.alternate, transform)
|
|
284
|
-
};
|
|
285
|
-
case "MemberExpr": return {
|
|
286
|
-
...node,
|
|
287
|
-
object: transformPlaceholders(node.object, transform),
|
|
288
|
-
property: node.computed ? transformPlaceholders(node.property, transform) : node.property
|
|
289
|
-
};
|
|
290
|
-
case "CallExpr": return {
|
|
291
|
-
...node,
|
|
292
|
-
callee: transformPlaceholders(node.callee, transform),
|
|
293
|
-
arguments: node.arguments.map((arg) => transformPlaceholders(arg, transform))
|
|
294
|
-
};
|
|
295
|
-
case "ArrayExpr": return {
|
|
296
|
-
...node,
|
|
297
|
-
elements: node.elements.map((el) => transformPlaceholders(el, transform))
|
|
298
|
-
};
|
|
299
|
-
case "ObjectExpr": return {
|
|
300
|
-
...node,
|
|
301
|
-
properties: node.properties.map((prop) => ({
|
|
302
|
-
...prop,
|
|
303
|
-
key: prop.computed ? transformPlaceholders(prop.key, transform) : prop.key,
|
|
304
|
-
value: transformPlaceholders(prop.value, transform)
|
|
305
|
-
}))
|
|
306
|
-
};
|
|
307
439
|
case "ArrowFunctionExpr": {
|
|
308
|
-
const
|
|
309
|
-
|
|
440
|
+
const paramNames = node.params.filter((p) => p.type === "Identifier");
|
|
441
|
+
const nextShadowedNames = paramNames.length === 0 ? shadowedNames : new Set([...shadowedNames ?? [], ...paramNames.map((param) => param.name)]);
|
|
442
|
+
const body = transformExprIdentifiersWithScope(node.body, nameToId, nameToExprAST, refCounts, candidateRefs, nextShadowedNames);
|
|
443
|
+
return body === node.body ? node : {
|
|
310
444
|
...node,
|
|
311
|
-
body
|
|
445
|
+
body
|
|
312
446
|
};
|
|
313
447
|
}
|
|
314
448
|
default: return node;
|
|
315
449
|
}
|
|
316
450
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
function countIdentifierReferences(node, counts) {
|
|
322
|
-
switch (node.type) {
|
|
323
|
-
case "Identifier":
|
|
324
|
-
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
325
|
-
break;
|
|
326
|
-
case "Placeholder": break;
|
|
327
|
-
case "BinaryExpr":
|
|
328
|
-
countIdentifierReferences(node.left, counts);
|
|
329
|
-
countIdentifierReferences(node.right, counts);
|
|
330
|
-
break;
|
|
331
|
-
case "UnaryExpr":
|
|
332
|
-
countIdentifierReferences(node.argument, counts);
|
|
333
|
-
break;
|
|
334
|
-
case "ConditionalExpr":
|
|
335
|
-
countIdentifierReferences(node.test, counts);
|
|
336
|
-
countIdentifierReferences(node.consequent, counts);
|
|
337
|
-
countIdentifierReferences(node.alternate, counts);
|
|
338
|
-
break;
|
|
339
|
-
case "MemberExpr":
|
|
340
|
-
countIdentifierReferences(node.object, counts);
|
|
341
|
-
if (node.computed) countIdentifierReferences(node.property, counts);
|
|
342
|
-
break;
|
|
343
|
-
case "CallExpr":
|
|
344
|
-
countIdentifierReferences(node.callee, counts);
|
|
345
|
-
for (const arg of node.arguments) countIdentifierReferences(arg, counts);
|
|
346
|
-
break;
|
|
347
|
-
case "ArrayExpr":
|
|
348
|
-
for (const el of node.elements) countIdentifierReferences(el, counts);
|
|
349
|
-
break;
|
|
350
|
-
case "ObjectExpr":
|
|
351
|
-
for (const prop of node.properties) {
|
|
352
|
-
if (prop.computed) countIdentifierReferences(prop.key, counts);
|
|
353
|
-
countIdentifierReferences(prop.value, counts);
|
|
354
|
-
}
|
|
355
|
-
break;
|
|
356
|
-
case "ArrowFunctionExpr":
|
|
357
|
-
{
|
|
358
|
-
const paramNames = new Set(node.params.filter((p) => p.type === "Identifier").map((p) => p.name));
|
|
359
|
-
const bodyCounts = /* @__PURE__ */ new Map();
|
|
360
|
-
countIdentifierReferences(node.body, bodyCounts);
|
|
361
|
-
for (const [name, count] of bodyCounts) if (!paramNames.has(name)) counts.set(name, (counts.get(name) ?? 0) + count);
|
|
362
|
-
}
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
451
|
+
function replaceNode(target, replacement) {
|
|
452
|
+
const record = target;
|
|
453
|
+
for (const key of Object.keys(record)) delete record[key];
|
|
454
|
+
Object.assign(record, replacement);
|
|
365
455
|
}
|
|
366
456
|
|
|
367
457
|
//#endregion
|
|
@@ -1336,6 +1426,7 @@ function expr(context) {
|
|
|
1336
1426
|
return (source) => {
|
|
1337
1427
|
const deps = /* @__PURE__ */ new Set();
|
|
1338
1428
|
const nameToId = /* @__PURE__ */ new Map();
|
|
1429
|
+
const nameToExprAST = /* @__PURE__ */ new Map();
|
|
1339
1430
|
for (const [name, value] of Object.entries(context)) {
|
|
1340
1431
|
let id = getVariableId(value);
|
|
1341
1432
|
if (!id && (typeof value === "object" || typeof value === "function") && value !== null) {
|
|
@@ -1348,32 +1439,15 @@ function expr(context) {
|
|
|
1348
1439
|
} else {
|
|
1349
1440
|
const meta = (typeof value === "object" || typeof value === "function") && value !== null ? getProxyMetadata(value) : void 0;
|
|
1350
1441
|
if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
|
|
1442
|
+
if (meta?.ast) nameToExprAST.set(name, meta.ast);
|
|
1351
1443
|
}
|
|
1352
1444
|
}
|
|
1353
|
-
const nameToExprAST = /* @__PURE__ */ new Map();
|
|
1354
|
-
for (const [name, value] of Object.entries(context)) if ((typeof value === "object" || typeof value === "function") && value !== null) {
|
|
1355
|
-
if (nameToId.has(name)) continue;
|
|
1356
|
-
const meta = getProxyMetadata(value);
|
|
1357
|
-
if (meta?.ast) nameToExprAST.set(name, meta.ast);
|
|
1358
|
-
}
|
|
1359
1445
|
const ast = parse(source);
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
if (id) return {
|
|
1366
|
-
type: "Placeholder",
|
|
1367
|
-
id
|
|
1368
|
-
};
|
|
1369
|
-
const exprAST = nameToExprAST.get(name);
|
|
1370
|
-
if (exprAST) {
|
|
1371
|
-
if ((refCounts.get(name) ?? 0) <= 1) return exprAST;
|
|
1372
|
-
deferredAsts.set(name, exprAST);
|
|
1373
|
-
return name;
|
|
1374
|
-
}
|
|
1375
|
-
return name;
|
|
1376
|
-
}), deps, deferredAsts.size > 0 ? deferredAsts : void 0);
|
|
1446
|
+
const { ast: transformedAst, deferredAsts } = nameToExprAST.size === 0 ? {
|
|
1447
|
+
ast: transformExprVariables(ast, nameToId),
|
|
1448
|
+
deferredAsts: void 0
|
|
1449
|
+
} : transformExprIdentifiers(ast, nameToId, nameToExprAST);
|
|
1450
|
+
return createProxyExpressionWithAST(transformedAst, deps, deferredAsts);
|
|
1377
1451
|
};
|
|
1378
1452
|
}
|
|
1379
1453
|
|
|
@@ -1635,12 +1709,10 @@ const BRANCH_CONDITIONS = {
|
|
|
1635
1709
|
* ```
|
|
1636
1710
|
*/
|
|
1637
1711
|
function compile(expression, variables, _options = {}) {
|
|
1638
|
-
|
|
1639
|
-
if ((typeof expression === "object" || typeof expression === "function") && expression !== null) deferredAsts = getProxyMetadata(expression)?.deferredAsts;
|
|
1640
|
-
const ast = serializeArgumentToAST(expression);
|
|
1712
|
+
const { ast, deferredAsts } = normalizeCompileInput(expression);
|
|
1641
1713
|
const variableOrder = [];
|
|
1642
1714
|
const variableToIndex = /* @__PURE__ */ new Map();
|
|
1643
|
-
const
|
|
1715
|
+
const placeholderNames = /* @__PURE__ */ new Map();
|
|
1644
1716
|
for (const [name, value] of Object.entries(variables)) {
|
|
1645
1717
|
if (!variableToIndex.has(name)) {
|
|
1646
1718
|
const index = variableOrder.length;
|
|
@@ -1648,38 +1720,122 @@ function compile(expression, variables, _options = {}) {
|
|
|
1648
1720
|
variableOrder.push(name);
|
|
1649
1721
|
}
|
|
1650
1722
|
const id = getVariableId(value);
|
|
1651
|
-
if (id)
|
|
1723
|
+
if (id) placeholderNames.set(id, `$[${variableToIndex.get(name)}]`);
|
|
1652
1724
|
}
|
|
1653
1725
|
const topLevelExprs = [];
|
|
1726
|
+
const baseScope = { placeholderNames };
|
|
1654
1727
|
const ctx = {
|
|
1655
1728
|
nextParamIndex: 0,
|
|
1656
1729
|
expressionStack: [topLevelExprs],
|
|
1657
1730
|
nextIndex: variableOrder.length,
|
|
1658
|
-
variableCount: variableOrder.length
|
|
1731
|
+
variableCount: variableOrder.length,
|
|
1732
|
+
baseScope
|
|
1659
1733
|
};
|
|
1660
1734
|
const deferredIndexMap = /* @__PURE__ */ new Map();
|
|
1661
|
-
if (deferredAsts && deferredAsts.size > 0) compileDeferredExprs(deferredAsts, ctx,
|
|
1662
|
-
const placeholderTransformed = transformPlaceholders(ast, (id) => {
|
|
1663
|
-
const name = symbolToName.get(id);
|
|
1664
|
-
if (!name) return null;
|
|
1665
|
-
const index = variableToIndex.get(name);
|
|
1666
|
-
return index !== void 0 ? `$[${index}]` : null;
|
|
1667
|
-
});
|
|
1735
|
+
if (deferredAsts && deferredAsts.size > 0) compileDeferredExprs(deferredAsts, ctx, baseScope, deferredIndexMap);
|
|
1668
1736
|
const undefinedVars = [];
|
|
1669
|
-
const transformed =
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
if (index !== void 0) return `$[${index}]`;
|
|
1674
|
-
const deferredIdx = deferredIndexMap.get(name);
|
|
1675
|
-
if (deferredIdx !== void 0) return `$[${deferredIdx}]`;
|
|
1676
|
-
if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
|
|
1677
|
-
return name;
|
|
1737
|
+
const transformed = rewriteAstForCompile(ast, {
|
|
1738
|
+
...baseScope,
|
|
1739
|
+
deferredIndexMap,
|
|
1740
|
+
undefinedVars
|
|
1678
1741
|
});
|
|
1679
1742
|
if (undefinedVars.length > 0) throw new Error(`Undefined variable(s): ${[...new Set(undefinedVars)].join(", ")}`);
|
|
1680
|
-
compileAst(transformed, ctx);
|
|
1743
|
+
compileAst(transformed, ctx, baseScope);
|
|
1681
1744
|
return [variableOrder, ...topLevelExprs];
|
|
1682
1745
|
}
|
|
1746
|
+
function normalizeCompileInput(expression) {
|
|
1747
|
+
const state = {
|
|
1748
|
+
deferredAsts: /* @__PURE__ */ new Map(),
|
|
1749
|
+
nextDeferredGroup: 0
|
|
1750
|
+
};
|
|
1751
|
+
return {
|
|
1752
|
+
ast: normalizeCompileValueToAST(expression, state),
|
|
1753
|
+
deferredAsts: state.deferredAsts.size > 0 ? state.deferredAsts : void 0
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
function normalizeCompileValueToAST(value, state) {
|
|
1757
|
+
if ((typeof value === "object" || typeof value === "function") && value !== null) {
|
|
1758
|
+
const meta = getProxyMetadata(value);
|
|
1759
|
+
if (meta) {
|
|
1760
|
+
if (meta.ast) {
|
|
1761
|
+
if (meta.deferredAsts && meta.deferredAsts.size > 0) {
|
|
1762
|
+
const renamed = renameDeferredExpressions(meta.ast, meta.deferredAsts, state.nextDeferredGroup++);
|
|
1763
|
+
for (const [name, ast] of renamed.deferredAsts) state.deferredAsts.set(name, ast);
|
|
1764
|
+
return renamed.ast;
|
|
1765
|
+
}
|
|
1766
|
+
return meta.ast;
|
|
1767
|
+
}
|
|
1768
|
+
if (meta.rootVariable) return {
|
|
1769
|
+
type: "Placeholder",
|
|
1770
|
+
id: meta.rootVariable
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
if (Array.isArray(value)) return {
|
|
1775
|
+
type: "ArrayExpr",
|
|
1776
|
+
elements: value.map((item) => normalizeCompileValueToAST(item, state))
|
|
1777
|
+
};
|
|
1778
|
+
if (value instanceof Map) return {
|
|
1779
|
+
type: "CallExpr",
|
|
1780
|
+
callee: {
|
|
1781
|
+
type: "Identifier",
|
|
1782
|
+
name: "Map"
|
|
1783
|
+
},
|
|
1784
|
+
arguments: [{
|
|
1785
|
+
type: "ArrayExpr",
|
|
1786
|
+
elements: Array.from(value, ([key, entryValue]) => ({
|
|
1787
|
+
type: "ArrayExpr",
|
|
1788
|
+
elements: [normalizeCompileValueToAST(key, state), normalizeCompileValueToAST(entryValue, state)]
|
|
1789
|
+
}))
|
|
1790
|
+
}],
|
|
1791
|
+
optional: false
|
|
1792
|
+
};
|
|
1793
|
+
if (value instanceof Set) return {
|
|
1794
|
+
type: "CallExpr",
|
|
1795
|
+
callee: {
|
|
1796
|
+
type: "Identifier",
|
|
1797
|
+
name: "Set"
|
|
1798
|
+
},
|
|
1799
|
+
arguments: [{
|
|
1800
|
+
type: "ArrayExpr",
|
|
1801
|
+
elements: Array.from(value, (item) => normalizeCompileValueToAST(item, state))
|
|
1802
|
+
}],
|
|
1803
|
+
optional: false
|
|
1804
|
+
};
|
|
1805
|
+
if (isRecursivelySerializableObject(value)) return {
|
|
1806
|
+
type: "ObjectExpr",
|
|
1807
|
+
properties: Object.entries(value).map(([key, entryValue]) => ({
|
|
1808
|
+
key: /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? {
|
|
1809
|
+
type: "Identifier",
|
|
1810
|
+
name: key
|
|
1811
|
+
} : {
|
|
1812
|
+
type: "StringLiteral",
|
|
1813
|
+
value: key,
|
|
1814
|
+
quote: "\""
|
|
1815
|
+
},
|
|
1816
|
+
value: normalizeCompileValueToAST(entryValue, state),
|
|
1817
|
+
computed: false,
|
|
1818
|
+
shorthand: false
|
|
1819
|
+
}))
|
|
1820
|
+
};
|
|
1821
|
+
return serializeArgumentToAST(value);
|
|
1822
|
+
}
|
|
1823
|
+
function isRecursivelySerializableObject(value) {
|
|
1824
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1825
|
+
if (value instanceof Date || value instanceof RegExp || typeof URL !== "undefined" && value instanceof URL || typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams || value instanceof ArrayBuffer || value instanceof DataView || ArrayBuffer.isView(value)) return false;
|
|
1826
|
+
return true;
|
|
1827
|
+
}
|
|
1828
|
+
function renameDeferredExpressions(ast, deferredAsts, groupIndex) {
|
|
1829
|
+
const renamedNames = /* @__PURE__ */ new Map();
|
|
1830
|
+
for (const name of deferredAsts.keys()) renamedNames.set(name, `__deferred_${groupIndex}_${name}`);
|
|
1831
|
+
const renameNode = (node) => transformIdentifiers(node, (name) => renamedNames.get(name) ?? name);
|
|
1832
|
+
const renamedDeferredAsts = /* @__PURE__ */ new Map();
|
|
1833
|
+
for (const [name, deferredAst] of deferredAsts) renamedDeferredAsts.set(renamedNames.get(name), renameNode(deferredAst));
|
|
1834
|
+
return {
|
|
1835
|
+
ast: renameNode(ast),
|
|
1836
|
+
deferredAsts: renamedDeferredAsts
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1683
1839
|
/**
|
|
1684
1840
|
* 获取当前表达式列表
|
|
1685
1841
|
*/
|
|
@@ -1690,7 +1846,7 @@ function currentExprs(ctx) {
|
|
|
1690
1846
|
* 编译推迟的子表达式(因引用计数 >1 未内联)
|
|
1691
1847
|
* 使用递归处理,自动按依赖拓扑顺序编译
|
|
1692
1848
|
*/
|
|
1693
|
-
function compileDeferredExprs(deferredAsts, ctx,
|
|
1849
|
+
function compileDeferredExprs(deferredAsts, ctx, baseScope, deferredIndexMap) {
|
|
1694
1850
|
const processing = /* @__PURE__ */ new Set();
|
|
1695
1851
|
function compileOne(name) {
|
|
1696
1852
|
if (processing.has(name)) throw new Error(`Circular reference in deferred expressions: ${name}`);
|
|
@@ -1699,91 +1855,175 @@ function compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex,
|
|
|
1699
1855
|
const ast = deferredAsts.get(name);
|
|
1700
1856
|
if (!ast) throw new Error(`Unknown deferred expression: ${name}`);
|
|
1701
1857
|
processing.add(name);
|
|
1702
|
-
const t1 = transformPlaceholders(ast, (id) => {
|
|
1703
|
-
const vName = symbolToName.get(id);
|
|
1704
|
-
if (!vName) return null;
|
|
1705
|
-
const idx = variableToIndex.get(vName);
|
|
1706
|
-
return idx !== void 0 ? `$[${idx}]` : null;
|
|
1707
|
-
});
|
|
1708
1858
|
const undefinedVars = [];
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
if (!ALLOWED_GLOBALS.has(n)) undefinedVars.push(n);
|
|
1716
|
-
return n;
|
|
1859
|
+
const transformed = rewriteAstForCompile(ast, {
|
|
1860
|
+
...baseScope,
|
|
1861
|
+
deferredAsts,
|
|
1862
|
+
deferredIndexMap,
|
|
1863
|
+
onDeferredReference: compileOne,
|
|
1864
|
+
undefinedVars
|
|
1717
1865
|
});
|
|
1718
1866
|
if (undefinedVars.length > 0) throw new Error(`Undefined variable(s) in deferred expression "${name}": ${[...new Set(undefinedVars)].join(", ")}`);
|
|
1719
|
-
const idx = compileAst(
|
|
1867
|
+
const idx = compileAst(transformed, ctx, baseScope);
|
|
1720
1868
|
deferredIndexMap.set(name, idx);
|
|
1721
1869
|
processing.delete(name);
|
|
1722
1870
|
return idx;
|
|
1723
1871
|
}
|
|
1724
1872
|
for (const name of deferredAsts.keys()) compileOne(name);
|
|
1725
1873
|
}
|
|
1874
|
+
function rewriteAstForCompile(node, scope) {
|
|
1875
|
+
switch (node.type) {
|
|
1876
|
+
case "Placeholder": {
|
|
1877
|
+
const name = scope.placeholderNames.get(node.id);
|
|
1878
|
+
return name ? {
|
|
1879
|
+
type: "Identifier",
|
|
1880
|
+
name
|
|
1881
|
+
} : node;
|
|
1882
|
+
}
|
|
1883
|
+
case "Identifier": {
|
|
1884
|
+
if (scope.shadowedNames?.has(node.name)) return node;
|
|
1885
|
+
const resolved = resolveIdentifierName(node.name, scope);
|
|
1886
|
+
return resolved === node.name ? node : {
|
|
1887
|
+
...node,
|
|
1888
|
+
name: resolved
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
case "BinaryExpr": {
|
|
1892
|
+
const left = rewriteAstForCompile(node.left, scope);
|
|
1893
|
+
const right = rewriteAstForCompile(node.right, scope);
|
|
1894
|
+
return left === node.left && right === node.right ? node : {
|
|
1895
|
+
...node,
|
|
1896
|
+
left,
|
|
1897
|
+
right
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
case "UnaryExpr": {
|
|
1901
|
+
const argument = rewriteAstForCompile(node.argument, scope);
|
|
1902
|
+
return argument === node.argument ? node : {
|
|
1903
|
+
...node,
|
|
1904
|
+
argument
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
case "ConditionalExpr": {
|
|
1908
|
+
const test = rewriteAstForCompile(node.test, scope);
|
|
1909
|
+
const consequent = rewriteAstForCompile(node.consequent, scope);
|
|
1910
|
+
const alternate = rewriteAstForCompile(node.alternate, scope);
|
|
1911
|
+
return test === node.test && consequent === node.consequent && alternate === node.alternate ? node : {
|
|
1912
|
+
...node,
|
|
1913
|
+
test,
|
|
1914
|
+
consequent,
|
|
1915
|
+
alternate
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
case "MemberExpr": {
|
|
1919
|
+
const object = rewriteAstForCompile(node.object, scope);
|
|
1920
|
+
const property = node.computed ? rewriteAstForCompile(node.property, scope) : node.property;
|
|
1921
|
+
return object === node.object && property === node.property ? node : {
|
|
1922
|
+
...node,
|
|
1923
|
+
object,
|
|
1924
|
+
property
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
case "CallExpr": {
|
|
1928
|
+
const callee = rewriteAstForCompile(node.callee, scope);
|
|
1929
|
+
const arguments_ = mapAstNodes(node.arguments, (arg) => rewriteAstForCompile(arg, scope));
|
|
1930
|
+
return callee === node.callee && arguments_ === node.arguments ? node : {
|
|
1931
|
+
...node,
|
|
1932
|
+
callee,
|
|
1933
|
+
arguments: arguments_
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
case "ArrayExpr": {
|
|
1937
|
+
const elements = mapAstNodes(node.elements, (el) => rewriteAstForCompile(el, scope));
|
|
1938
|
+
return elements === node.elements ? node : {
|
|
1939
|
+
...node,
|
|
1940
|
+
elements
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
case "ObjectExpr": {
|
|
1944
|
+
const properties = mapObjectProperties(node.properties, (prop) => {
|
|
1945
|
+
const key = prop.computed ? rewriteAstForCompile(prop.key, scope) : prop.key;
|
|
1946
|
+
const value = rewriteAstForCompile(prop.value, scope);
|
|
1947
|
+
return key === prop.key && value === prop.value ? prop : {
|
|
1948
|
+
...prop,
|
|
1949
|
+
key,
|
|
1950
|
+
value
|
|
1951
|
+
};
|
|
1952
|
+
});
|
|
1953
|
+
return properties === node.properties ? node : {
|
|
1954
|
+
...node,
|
|
1955
|
+
properties
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
case "ArrowFunctionExpr": return node;
|
|
1959
|
+
default: return node;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
function resolveIdentifierName(name, scope) {
|
|
1963
|
+
if (/^\$\[\d+\]$/.test(name) || /^_\d+$/.test(name)) return name;
|
|
1964
|
+
const deferredIdx = scope.deferredIndexMap?.get(name);
|
|
1965
|
+
if (deferredIdx !== void 0) return `$[${deferredIdx}]`;
|
|
1966
|
+
if (scope.deferredAsts?.has(name) && scope.onDeferredReference) return `$[${scope.onDeferredReference(name)}]`;
|
|
1967
|
+
if (!ALLOWED_GLOBALS.has(name)) scope.undefinedVars?.push(name);
|
|
1968
|
+
return name;
|
|
1969
|
+
}
|
|
1970
|
+
function extendShadowedNames(shadowedNames, names) {
|
|
1971
|
+
if (names.length === 0) return shadowedNames;
|
|
1972
|
+
return new Set([...shadowedNames ?? [], ...names]);
|
|
1973
|
+
}
|
|
1974
|
+
function mapAstNodes(nodes, transform) {
|
|
1975
|
+
let result;
|
|
1976
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1977
|
+
const node = nodes[i];
|
|
1978
|
+
const transformed = transform(node);
|
|
1979
|
+
if (result) {
|
|
1980
|
+
result.push(transformed);
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
if (transformed !== node) {
|
|
1984
|
+
result = nodes.slice(0, i);
|
|
1985
|
+
result.push(transformed);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
return result ?? nodes;
|
|
1989
|
+
}
|
|
1990
|
+
function mapObjectProperties(properties, transform) {
|
|
1991
|
+
let result;
|
|
1992
|
+
for (let i = 0; i < properties.length; i++) {
|
|
1993
|
+
const property = properties[i];
|
|
1994
|
+
const transformed = transform(property);
|
|
1995
|
+
if (result) {
|
|
1996
|
+
result.push(transformed);
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
if (transformed !== property) {
|
|
2000
|
+
result = properties.slice(0, i);
|
|
2001
|
+
result.push(transformed);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
return result ?? properties;
|
|
2005
|
+
}
|
|
1726
2006
|
/**
|
|
1727
2007
|
* 提取 AST 中所有 ArrowFunctionExpr 节点,编译为 FnNode,
|
|
1728
2008
|
* 并将原始位置替换为 $[N] 标识符引用。
|
|
1729
2009
|
*/
|
|
1730
|
-
function
|
|
1731
|
-
|
|
1732
|
-
|
|
2010
|
+
function compileAst(node, ctx, scope) {
|
|
2011
|
+
if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node, ctx, scope);
|
|
2012
|
+
if (node.type === "ConditionalExpr") return compileConditional(node, ctx, scope);
|
|
2013
|
+
if (node.type === "ArrowFunctionExpr") return compileArrowFunction(node, ctx, scope);
|
|
2014
|
+
const exprStr = generate(node, { rewriteNode: (current) => {
|
|
2015
|
+
if (current.type !== "ArrowFunctionExpr") return current;
|
|
2016
|
+
return {
|
|
1733
2017
|
type: "Identifier",
|
|
1734
|
-
name: `$[${compileArrowFunction(
|
|
1735
|
-
};
|
|
1736
|
-
case "BinaryExpr": return {
|
|
1737
|
-
...node,
|
|
1738
|
-
left: extractAndCompileArrowFunctions(node.left, ctx),
|
|
1739
|
-
right: extractAndCompileArrowFunctions(node.right, ctx)
|
|
2018
|
+
name: `$[${compileArrowFunction(current, ctx, scope)}]`
|
|
1740
2019
|
};
|
|
1741
|
-
|
|
1742
|
-
...node,
|
|
1743
|
-
argument: extractAndCompileArrowFunctions(node.argument, ctx)
|
|
1744
|
-
};
|
|
1745
|
-
case "ConditionalExpr": return {
|
|
1746
|
-
...node,
|
|
1747
|
-
test: extractAndCompileArrowFunctions(node.test, ctx),
|
|
1748
|
-
consequent: extractAndCompileArrowFunctions(node.consequent, ctx),
|
|
1749
|
-
alternate: extractAndCompileArrowFunctions(node.alternate, ctx)
|
|
1750
|
-
};
|
|
1751
|
-
case "MemberExpr": return {
|
|
1752
|
-
...node,
|
|
1753
|
-
object: extractAndCompileArrowFunctions(node.object, ctx),
|
|
1754
|
-
property: node.computed ? extractAndCompileArrowFunctions(node.property, ctx) : node.property
|
|
1755
|
-
};
|
|
1756
|
-
case "CallExpr": return {
|
|
1757
|
-
...node,
|
|
1758
|
-
callee: extractAndCompileArrowFunctions(node.callee, ctx),
|
|
1759
|
-
arguments: node.arguments.map((arg) => extractAndCompileArrowFunctions(arg, ctx))
|
|
1760
|
-
};
|
|
1761
|
-
case "ArrayExpr": return {
|
|
1762
|
-
...node,
|
|
1763
|
-
elements: node.elements.map((el) => extractAndCompileArrowFunctions(el, ctx))
|
|
1764
|
-
};
|
|
1765
|
-
case "ObjectExpr": return {
|
|
1766
|
-
...node,
|
|
1767
|
-
properties: node.properties.map((prop) => ({
|
|
1768
|
-
...prop,
|
|
1769
|
-
key: prop.computed ? extractAndCompileArrowFunctions(prop.key, ctx) : prop.key,
|
|
1770
|
-
value: extractAndCompileArrowFunctions(prop.value, ctx)
|
|
1771
|
-
}))
|
|
1772
|
-
};
|
|
1773
|
-
default: return node;
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
function compileAst(node, ctx) {
|
|
1777
|
-
if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node, ctx);
|
|
1778
|
-
if (node.type === "ConditionalExpr") return compileConditional(node, ctx);
|
|
1779
|
-
if (node.type === "ArrowFunctionExpr") return compileArrowFunction(node, ctx);
|
|
1780
|
-
const exprStr = generate(extractAndCompileArrowFunctions(node, ctx));
|
|
2020
|
+
} });
|
|
1781
2021
|
currentExprs(ctx).push(exprStr);
|
|
1782
2022
|
return ctx.nextIndex++;
|
|
1783
2023
|
}
|
|
1784
|
-
function compileShortCircuit(node, ctx) {
|
|
2024
|
+
function compileShortCircuit(node, ctx, scope) {
|
|
1785
2025
|
const exprs = currentExprs(ctx);
|
|
1786
|
-
const leftIdx = compileAst(node.left, ctx);
|
|
2026
|
+
const leftIdx = compileAst(node.left, ctx, scope);
|
|
1787
2027
|
const condition = BRANCH_CONDITIONS[node.operator]?.(leftIdx);
|
|
1788
2028
|
if (!condition) throw new Error(`Unexpected operator: ${node.operator}`);
|
|
1789
2029
|
const branchIdx = exprs.length;
|
|
@@ -1793,16 +2033,16 @@ function compileShortCircuit(node, ctx) {
|
|
|
1793
2033
|
0
|
|
1794
2034
|
]);
|
|
1795
2035
|
ctx.nextIndex++;
|
|
1796
|
-
compileAst(node.right, ctx);
|
|
2036
|
+
compileAst(node.right, ctx, scope);
|
|
1797
2037
|
const skipCount = exprs.length - branchIdx - 1;
|
|
1798
2038
|
exprs[branchIdx][2] = skipCount;
|
|
1799
2039
|
const phiIdx = ctx.nextIndex++;
|
|
1800
2040
|
exprs.push(["phi"]);
|
|
1801
2041
|
return phiIdx;
|
|
1802
2042
|
}
|
|
1803
|
-
function compileConditional(node, ctx) {
|
|
2043
|
+
function compileConditional(node, ctx, scope) {
|
|
1804
2044
|
const exprs = currentExprs(ctx);
|
|
1805
|
-
const testIdx = compileAst(node.test, ctx);
|
|
2045
|
+
const testIdx = compileAst(node.test, ctx, scope);
|
|
1806
2046
|
const branchIdx = exprs.length;
|
|
1807
2047
|
exprs.push([
|
|
1808
2048
|
"br",
|
|
@@ -1810,11 +2050,11 @@ function compileConditional(node, ctx) {
|
|
|
1810
2050
|
0
|
|
1811
2051
|
]);
|
|
1812
2052
|
ctx.nextIndex++;
|
|
1813
|
-
compileAst(node.alternate, ctx);
|
|
2053
|
+
compileAst(node.alternate, ctx, scope);
|
|
1814
2054
|
const jmpIdx = exprs.length;
|
|
1815
2055
|
exprs.push(["jmp", 0]);
|
|
1816
2056
|
ctx.nextIndex++;
|
|
1817
|
-
compileAst(node.consequent, ctx);
|
|
2057
|
+
compileAst(node.consequent, ctx, scope);
|
|
1818
2058
|
const thenEndIdx = exprs.length;
|
|
1819
2059
|
exprs[branchIdx][2] = jmpIdx - branchIdx;
|
|
1820
2060
|
exprs[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
|
|
@@ -1822,14 +2062,21 @@ function compileConditional(node, ctx) {
|
|
|
1822
2062
|
exprs.push(["phi"]);
|
|
1823
2063
|
return phiIdx;
|
|
1824
2064
|
}
|
|
1825
|
-
function compileArrowFunction(node, ctx) {
|
|
2065
|
+
function compileArrowFunction(node, ctx, scope) {
|
|
1826
2066
|
const fnIndex = ctx.nextIndex++;
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1829
|
-
const
|
|
2067
|
+
const placeholderNames = new Map(scope.placeholderNames);
|
|
2068
|
+
const shadowedNames = [];
|
|
2069
|
+
for (const param of node.params) if (param.type === "Placeholder") placeholderNames.set(param.id, `_${ctx.nextParamIndex++}`);
|
|
2070
|
+
else shadowedNames.push(param.name);
|
|
2071
|
+
const lambdaScope = {
|
|
2072
|
+
...scope,
|
|
2073
|
+
placeholderNames,
|
|
2074
|
+
shadowedNames: extendShadowedNames(scope.shadowedNames, shadowedNames)
|
|
2075
|
+
};
|
|
2076
|
+
const transformedBody = rewriteAstForCompile(node.body, lambdaScope);
|
|
1830
2077
|
const lambdaStmts = [];
|
|
1831
2078
|
ctx.expressionStack.push(lambdaStmts);
|
|
1832
|
-
compileAst(transformedBody, ctx);
|
|
2079
|
+
compileAst(transformedBody, ctx, lambdaScope);
|
|
1833
2080
|
ctx.expressionStack.pop();
|
|
1834
2081
|
const fnNode = [
|
|
1835
2082
|
"fn",
|
|
@@ -1843,9 +2090,40 @@ function compileArrowFunction(node, ctx) {
|
|
|
1843
2090
|
//#endregion
|
|
1844
2091
|
//#region src/core/evaluate.ts
|
|
1845
2092
|
/**
|
|
1846
|
-
*
|
|
2093
|
+
* 一级缓存:WeakMap - 使用 CompiledData 对象引用作为 key
|
|
2094
|
+
* 优势:O(1) 查找,无序列化开销,自动 GC
|
|
2095
|
+
* 适用:相同引用被重复使用的场景
|
|
2096
|
+
*/
|
|
2097
|
+
const weakEvaluatorCache = /* @__PURE__ */ new WeakMap();
|
|
2098
|
+
/**
|
|
2099
|
+
* 二级缓存:LRU Map - 使用编译数据指纹作为 key
|
|
2100
|
+
* 优势:支持内容相同但引用不同的对象,有大小限制避免内存泄漏
|
|
2101
|
+
* 适用:CompiledData 被频繁重建但内容重复的场景
|
|
1847
2102
|
*/
|
|
1848
|
-
const
|
|
2103
|
+
const MAX_LRU_CACHE_SIZE = 128;
|
|
2104
|
+
const lruEvaluatorCache = /* @__PURE__ */ new Map();
|
|
2105
|
+
/**
|
|
2106
|
+
* 从 LRU 缓存获取求值函数,并将命中项移至末尾(最近使用)
|
|
2107
|
+
*/
|
|
2108
|
+
function getFromLruCache(key) {
|
|
2109
|
+
const evaluator = lruEvaluatorCache.get(key);
|
|
2110
|
+
if (evaluator) {
|
|
2111
|
+
lruEvaluatorCache.delete(key);
|
|
2112
|
+
lruEvaluatorCache.set(key, evaluator);
|
|
2113
|
+
}
|
|
2114
|
+
return evaluator;
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* 向 LRU 缓存写入求值函数,超出限制时淘汰最久未使用的项
|
|
2118
|
+
*/
|
|
2119
|
+
function setToLruCache(key, evaluator) {
|
|
2120
|
+
if (lruEvaluatorCache.has(key)) lruEvaluatorCache.delete(key);
|
|
2121
|
+
else if (lruEvaluatorCache.size >= MAX_LRU_CACHE_SIZE) {
|
|
2122
|
+
const firstKey = lruEvaluatorCache.keys().next().value;
|
|
2123
|
+
if (firstKey !== void 0) lruEvaluatorCache.delete(firstKey);
|
|
2124
|
+
}
|
|
2125
|
+
lruEvaluatorCache.set(key, evaluator);
|
|
2126
|
+
}
|
|
1849
2127
|
/**
|
|
1850
2128
|
* 判断是否是 FnNode
|
|
1851
2129
|
*/
|
|
@@ -2006,21 +2284,29 @@ function translateControlFlow(expressions, variableCount) {
|
|
|
2006
2284
|
* ```
|
|
2007
2285
|
*/
|
|
2008
2286
|
function evaluate(data, values) {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
if (
|
|
2012
|
-
|
|
2013
|
-
if (
|
|
2014
|
-
|
|
2287
|
+
let evaluator = weakEvaluatorCache.get(data);
|
|
2288
|
+
let variableNames;
|
|
2289
|
+
if (evaluator) variableNames = data[0];
|
|
2290
|
+
else {
|
|
2291
|
+
if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
|
|
2292
|
+
variableNames = data[0];
|
|
2293
|
+
if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
|
|
2294
|
+
for (let i = 0; i < variableNames.length; i++) if (typeof variableNames[i] !== "string") throw new Error("Invalid compiled data: variable names must be strings");
|
|
2295
|
+
const cacheKey = JSON.stringify(data);
|
|
2296
|
+
evaluator = getFromLruCache(cacheKey);
|
|
2297
|
+
if (!evaluator) {
|
|
2298
|
+
const functionBody = buildEvaluatorFunctionBody(data.slice(1), variableNames.length);
|
|
2299
|
+
evaluator = new Function("$", functionBody);
|
|
2300
|
+
setToLruCache(cacheKey, evaluator);
|
|
2301
|
+
}
|
|
2302
|
+
weakEvaluatorCache.set(data, evaluator);
|
|
2015
2303
|
}
|
|
2304
|
+
const len = variableNames.length;
|
|
2016
2305
|
const valueArray = [];
|
|
2017
|
-
for (
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
const functionBody = buildEvaluatorFunctionBody(expressions, variableNames.length);
|
|
2022
|
-
evaluator = new Function("$", functionBody);
|
|
2023
|
-
evaluatorCache.set(cacheKey, evaluator);
|
|
2306
|
+
for (let i = 0; i < len; i++) {
|
|
2307
|
+
const name = variableNames[i];
|
|
2308
|
+
if (!(name in values)) throw new Error(`Missing required variable: ${name}`);
|
|
2309
|
+
valueArray.push(values[name]);
|
|
2024
2310
|
}
|
|
2025
2311
|
try {
|
|
2026
2312
|
return evaluator(valueArray);
|