@codehz/json-expr 0.5.1 → 0.5.3
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 +1328 -1353
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/ast-types.ts
|
|
2
2
|
const PRECEDENCE = {
|
|
3
3
|
"||": 1,
|
|
4
4
|
"??": 1,
|
|
@@ -27,1421 +27,1372 @@ const PRECEDENCE = {
|
|
|
27
27
|
"**": 11
|
|
28
28
|
};
|
|
29
29
|
const RIGHT_ASSOCIATIVE = new Set(["**"]);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
const BUILTIN_CONSTRUCTORS = new Set([
|
|
31
|
+
"Date",
|
|
32
|
+
"RegExp",
|
|
33
|
+
"URL",
|
|
34
|
+
"URLSearchParams",
|
|
35
|
+
"Map",
|
|
36
|
+
"Set",
|
|
37
|
+
"Int8Array",
|
|
38
|
+
"Uint8Array",
|
|
39
|
+
"Uint8ClampedArray",
|
|
40
|
+
"Int16Array",
|
|
41
|
+
"Uint16Array",
|
|
42
|
+
"Int32Array",
|
|
43
|
+
"Uint32Array",
|
|
44
|
+
"Float32Array",
|
|
45
|
+
"Float64Array",
|
|
46
|
+
"BigInt64Array",
|
|
47
|
+
"BigUint64Array",
|
|
48
|
+
"ArrayBuffer",
|
|
49
|
+
"DataView"
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/generate.ts
|
|
54
|
+
/**
|
|
55
|
+
* 创建新的生成上下文
|
|
56
|
+
*/
|
|
57
|
+
function createGenerateContext() {
|
|
58
|
+
return {
|
|
59
|
+
lambdaParamCounter: 0,
|
|
60
|
+
paramMapping: /* @__PURE__ */ new Map()
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 从 AST 生成规范化的代码
|
|
65
|
+
*/
|
|
66
|
+
function generate(node) {
|
|
67
|
+
return generateWithContext(node, createGenerateContext());
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 带上下文的代码生成
|
|
71
|
+
*/
|
|
72
|
+
function generateWithContext(node, ctx) {
|
|
73
|
+
switch (node.type) {
|
|
74
|
+
case "NumberLiteral": return node.raw;
|
|
75
|
+
case "StringLiteral": return JSON.stringify(node.value);
|
|
76
|
+
case "BooleanLiteral": return node.value ? "true" : "false";
|
|
77
|
+
case "NullLiteral": return "null";
|
|
78
|
+
case "Identifier": return node.name;
|
|
79
|
+
case "Placeholder": {
|
|
80
|
+
const mappedName = ctx.paramMapping.get(node.id);
|
|
81
|
+
if (mappedName) return mappedName;
|
|
82
|
+
return `$$${node.id.description}$$`;
|
|
83
|
+
}
|
|
84
|
+
case "BinaryExpr": {
|
|
85
|
+
const left = wrapIfNeededWithContext(node.left, node, "left", ctx);
|
|
86
|
+
const right = wrapIfNeededWithContext(node.right, node, "right", ctx);
|
|
87
|
+
if (node.operator === "in" || node.operator === "instanceof") return `${left} ${node.operator} ${right}`;
|
|
88
|
+
return `${left}${node.operator}${right}`;
|
|
89
|
+
}
|
|
90
|
+
case "UnaryExpr":
|
|
91
|
+
if (node.prefix) {
|
|
92
|
+
const arg = wrapIfNeededWithContext(node.argument, node, "argument", ctx);
|
|
93
|
+
if (node.operator === "typeof" || node.operator === "void") return `${node.operator} ${arg}`;
|
|
94
|
+
return `${node.operator}${arg}`;
|
|
95
|
+
}
|
|
96
|
+
return generateWithContext(node.argument, ctx) + node.operator;
|
|
97
|
+
case "ConditionalExpr": return `${wrapIfNeededWithContext(node.test, node, "test", ctx)}?${wrapIfNeededWithContext(node.consequent, node, "consequent", ctx)}:${wrapIfNeededWithContext(node.alternate, node, "alternate", ctx)}`;
|
|
98
|
+
case "MemberExpr": {
|
|
99
|
+
const object = wrapIfNeededWithContext(node.object, node, "object", ctx);
|
|
100
|
+
const property = generateWithContext(node.property, ctx);
|
|
101
|
+
return node.computed ? `${object}${node.optional ? "?." : ""}[${property}]` : `${object}${node.optional ? "?." : "."}${property}`;
|
|
102
|
+
}
|
|
103
|
+
case "CallExpr": {
|
|
104
|
+
const callee = wrapIfNeededWithContext(node.callee, node, "callee", ctx);
|
|
105
|
+
const args = node.arguments.map((arg) => generateWithContext(arg, ctx)).join(",");
|
|
106
|
+
return `${node.callee.type === "Identifier" && BUILTIN_CONSTRUCTORS.has(node.callee.name) ? "new " : ""}${callee}${node.optional ? "?." : ""}(${args})`;
|
|
107
|
+
}
|
|
108
|
+
case "ArrayExpr": return `[${node.elements.map((el) => generateWithContext(el, ctx)).join(",")}]`;
|
|
109
|
+
case "ObjectExpr": return `{${node.properties.map((prop) => {
|
|
110
|
+
if (prop.shorthand) return generateWithContext(prop.key, ctx);
|
|
111
|
+
return `${prop.computed ? `[${generateWithContext(prop.key, ctx)}]` : generateWithContext(prop.key, ctx)}:${generateWithContext(prop.value, ctx)}`;
|
|
112
|
+
}).join(",")}}`;
|
|
113
|
+
case "ArrowFunctionExpr": {
|
|
114
|
+
const paramNames = [];
|
|
115
|
+
const placeholderIds = [];
|
|
116
|
+
for (const param of node.params) if (param.type === "Placeholder") {
|
|
117
|
+
const uniqueName = `_${ctx.lambdaParamCounter++}`;
|
|
118
|
+
paramNames.push(uniqueName);
|
|
119
|
+
placeholderIds.push(param.id);
|
|
120
|
+
ctx.paramMapping.set(param.id, uniqueName);
|
|
121
|
+
} else paramNames.push(param.name);
|
|
122
|
+
const paramsStr = paramNames.length === 1 ? paramNames[0] : `(${paramNames.join(",")})`;
|
|
123
|
+
const body = node.body.type === "ObjectExpr" ? `(${generateWithContext(node.body, ctx)})` : generateWithContext(node.body, ctx);
|
|
124
|
+
for (const id of placeholderIds) ctx.paramMapping.delete(id);
|
|
125
|
+
return `${paramsStr}=>${body}`;
|
|
126
|
+
}
|
|
127
|
+
default: {
|
|
128
|
+
const nodeType = node.type ?? "unknown";
|
|
129
|
+
throw new Error(`Unknown node type: ${nodeType}`);
|
|
130
|
+
}
|
|
42
131
|
}
|
|
43
|
-
|
|
44
|
-
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 判断是否需要括号包裹,并生成代码(带上下文版本)
|
|
135
|
+
*/
|
|
136
|
+
function wrapIfNeededWithContext(child, parent, position, ctx) {
|
|
137
|
+
const code = generateWithContext(child, ctx);
|
|
138
|
+
if (needsParens(child, parent, position)) return `(${code})`;
|
|
139
|
+
return code;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 判断子节点是否需要括号
|
|
143
|
+
*/
|
|
144
|
+
function needsParens(child, parent, position) {
|
|
145
|
+
switch (parent.type) {
|
|
146
|
+
case "BinaryExpr":
|
|
147
|
+
if (child.type === "ConditionalExpr" || child.type === "UnaryExpr") return true;
|
|
148
|
+
if (child.type === "BinaryExpr") {
|
|
149
|
+
const childPrec = PRECEDENCE[child.operator] ?? 0;
|
|
150
|
+
const parentPrec = PRECEDENCE[parent.operator] ?? 0;
|
|
151
|
+
if (childPrec < parentPrec) return true;
|
|
152
|
+
if (childPrec === parentPrec && position === "right" && !RIGHT_ASSOCIATIVE.has(parent.operator)) return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
case "UnaryExpr": return position === "argument" && (child.type === "BinaryExpr" || child.type === "ConditionalExpr");
|
|
156
|
+
case "MemberExpr":
|
|
157
|
+
case "CallExpr":
|
|
158
|
+
if (position !== "object" && position !== "callee") return false;
|
|
159
|
+
if ([
|
|
160
|
+
"BinaryExpr",
|
|
161
|
+
"ConditionalExpr",
|
|
162
|
+
"UnaryExpr",
|
|
163
|
+
"ArrowFunctionExpr",
|
|
164
|
+
"ObjectExpr"
|
|
165
|
+
].includes(child.type)) return true;
|
|
166
|
+
if (child.type === "NumberLiteral" && parent.type === "MemberExpr" && !parent.computed) return !child.raw.includes(".") && !child.raw.includes("e") && !child.raw.includes("x");
|
|
167
|
+
return false;
|
|
168
|
+
case "ConditionalExpr": return position === "test" && child.type === "ConditionalExpr";
|
|
169
|
+
default: return false;
|
|
45
170
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
alternate
|
|
62
|
-
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 转换 AST 中的标识符
|
|
174
|
+
* 回调函数可以返回:
|
|
175
|
+
* - string: 替换标识符名称
|
|
176
|
+
* - ASTNode: 内联该 AST 节点(用于子表达式内联)
|
|
177
|
+
*/
|
|
178
|
+
function transformIdentifiers(node, transform) {
|
|
179
|
+
switch (node.type) {
|
|
180
|
+
case "Identifier": {
|
|
181
|
+
const result = transform(node.name);
|
|
182
|
+
return typeof result === "string" ? {
|
|
183
|
+
...node,
|
|
184
|
+
name: result
|
|
185
|
+
} : result;
|
|
63
186
|
}
|
|
64
|
-
return node;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
187
|
+
case "Placeholder": return node;
|
|
188
|
+
case "BinaryExpr": return {
|
|
189
|
+
...node,
|
|
190
|
+
left: transformIdentifiers(node.left, transform),
|
|
191
|
+
right: transformIdentifiers(node.right, transform)
|
|
192
|
+
};
|
|
193
|
+
case "UnaryExpr": return {
|
|
194
|
+
...node,
|
|
195
|
+
argument: transformIdentifiers(node.argument, transform)
|
|
196
|
+
};
|
|
197
|
+
case "ConditionalExpr": return {
|
|
198
|
+
...node,
|
|
199
|
+
test: transformIdentifiers(node.test, transform),
|
|
200
|
+
consequent: transformIdentifiers(node.consequent, transform),
|
|
201
|
+
alternate: transformIdentifiers(node.alternate, transform)
|
|
202
|
+
};
|
|
203
|
+
case "MemberExpr": return {
|
|
204
|
+
...node,
|
|
205
|
+
object: transformIdentifiers(node.object, transform),
|
|
206
|
+
property: node.computed ? transformIdentifiers(node.property, transform) : node.property
|
|
207
|
+
};
|
|
208
|
+
case "CallExpr": return {
|
|
209
|
+
...node,
|
|
210
|
+
callee: transformIdentifiers(node.callee, transform),
|
|
211
|
+
arguments: node.arguments.map((arg) => transformIdentifiers(arg, transform))
|
|
212
|
+
};
|
|
213
|
+
case "ArrayExpr": return {
|
|
214
|
+
...node,
|
|
215
|
+
elements: node.elements.map((el) => transformIdentifiers(el, transform))
|
|
216
|
+
};
|
|
217
|
+
case "ObjectExpr": return {
|
|
218
|
+
...node,
|
|
219
|
+
properties: node.properties.map((prop) => ({
|
|
220
|
+
...prop,
|
|
221
|
+
key: prop.computed ? transformIdentifiers(prop.key, transform) : prop.key,
|
|
222
|
+
value: transformIdentifiers(prop.value, transform)
|
|
223
|
+
}))
|
|
224
|
+
};
|
|
225
|
+
case "ArrowFunctionExpr": {
|
|
226
|
+
const paramNames = new Set(node.params.filter((p) => p.type === "Identifier").map((p) => p.name));
|
|
227
|
+
return {
|
|
228
|
+
...node,
|
|
229
|
+
body: transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name))
|
|
81
230
|
};
|
|
82
231
|
}
|
|
83
|
-
return
|
|
232
|
+
default: return node;
|
|
84
233
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 转换 AST 中的占位符节点
|
|
237
|
+
* 回调函数接收 symbol,返回 Identifier 节点的名称
|
|
238
|
+
* 如果返回 null/undefined,则保留原始 Placeholder 节点
|
|
239
|
+
*/
|
|
240
|
+
function transformPlaceholders(node, transform) {
|
|
241
|
+
switch (node.type) {
|
|
242
|
+
case "Placeholder": {
|
|
243
|
+
const name = transform(node.id);
|
|
244
|
+
return name != null ? {
|
|
245
|
+
type: "Identifier",
|
|
246
|
+
name
|
|
247
|
+
} : node;
|
|
97
248
|
}
|
|
98
|
-
|
|
99
|
-
|
|
249
|
+
case "Identifier": return node;
|
|
250
|
+
case "BinaryExpr": return {
|
|
251
|
+
...node,
|
|
252
|
+
left: transformPlaceholders(node.left, transform),
|
|
253
|
+
right: transformPlaceholders(node.right, transform)
|
|
254
|
+
};
|
|
255
|
+
case "UnaryExpr": return {
|
|
256
|
+
...node,
|
|
257
|
+
argument: transformPlaceholders(node.argument, transform)
|
|
258
|
+
};
|
|
259
|
+
case "ConditionalExpr": return {
|
|
260
|
+
...node,
|
|
261
|
+
test: transformPlaceholders(node.test, transform),
|
|
262
|
+
consequent: transformPlaceholders(node.consequent, transform),
|
|
263
|
+
alternate: transformPlaceholders(node.alternate, transform)
|
|
264
|
+
};
|
|
265
|
+
case "MemberExpr": return {
|
|
266
|
+
...node,
|
|
267
|
+
object: transformPlaceholders(node.object, transform),
|
|
268
|
+
property: node.computed ? transformPlaceholders(node.property, transform) : node.property
|
|
269
|
+
};
|
|
270
|
+
case "CallExpr": return {
|
|
271
|
+
...node,
|
|
272
|
+
callee: transformPlaceholders(node.callee, transform),
|
|
273
|
+
arguments: node.arguments.map((arg) => transformPlaceholders(arg, transform))
|
|
274
|
+
};
|
|
275
|
+
case "ArrayExpr": return {
|
|
276
|
+
...node,
|
|
277
|
+
elements: node.elements.map((el) => transformPlaceholders(el, transform))
|
|
278
|
+
};
|
|
279
|
+
case "ObjectExpr": return {
|
|
280
|
+
...node,
|
|
281
|
+
properties: node.properties.map((prop) => ({
|
|
282
|
+
...prop,
|
|
283
|
+
key: prop.computed ? transformPlaceholders(prop.key, transform) : prop.key,
|
|
284
|
+
value: transformPlaceholders(prop.value, transform)
|
|
285
|
+
}))
|
|
286
|
+
};
|
|
287
|
+
case "ArrowFunctionExpr": {
|
|
288
|
+
const paramSymbols = new Set(node.params.filter((p) => p.type === "Placeholder").map((p) => p.id));
|
|
100
289
|
return {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
argument: this.parseUnary(),
|
|
104
|
-
prefix: true
|
|
290
|
+
...node,
|
|
291
|
+
body: transformPlaceholders(node.body, (id) => paramSymbols.has(id) ? null : transform(id))
|
|
105
292
|
};
|
|
106
293
|
}
|
|
107
|
-
return
|
|
294
|
+
default: return node;
|
|
108
295
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/proxy-metadata.ts
|
|
300
|
+
/**
|
|
301
|
+
* 全局 WeakMap 存储
|
|
302
|
+
*/
|
|
303
|
+
const proxyMetadata = /* @__PURE__ */ new WeakMap();
|
|
304
|
+
/**
|
|
305
|
+
* 设置 Proxy 元数据
|
|
306
|
+
*/
|
|
307
|
+
function setProxyMetadata(proxy, metadata) {
|
|
308
|
+
proxyMetadata.set(proxy, metadata);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 获取 Proxy 元数据
|
|
312
|
+
*/
|
|
313
|
+
function getProxyMetadata(proxy) {
|
|
314
|
+
return proxyMetadata.get(proxy);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 检查对象是否是 Proxy variable
|
|
318
|
+
*/
|
|
319
|
+
function isProxyVariable(obj) {
|
|
320
|
+
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
321
|
+
return proxyMetadata.get(obj)?.type === "variable";
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 检查对象是否是 Proxy expression
|
|
325
|
+
*/
|
|
326
|
+
function isProxyExpression(obj) {
|
|
327
|
+
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
328
|
+
return proxyMetadata.get(obj)?.type === "expression";
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* 检查对象是否是任意 Proxy (variable 或 expression)
|
|
332
|
+
*/
|
|
333
|
+
function isProxy(obj) {
|
|
334
|
+
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
335
|
+
return proxyMetadata.has(obj);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/proxy-variable.ts
|
|
340
|
+
const typedArrayConstructors = [
|
|
341
|
+
"Int8Array",
|
|
342
|
+
"Uint8Array",
|
|
343
|
+
"Uint8ClampedArray",
|
|
344
|
+
"Int16Array",
|
|
345
|
+
"Uint16Array",
|
|
346
|
+
"Int32Array",
|
|
347
|
+
"Uint32Array",
|
|
348
|
+
"Float32Array",
|
|
349
|
+
"Float64Array",
|
|
350
|
+
"BigInt64Array",
|
|
351
|
+
"BigUint64Array"
|
|
352
|
+
];
|
|
353
|
+
/**
|
|
354
|
+
* 创建占位符 AST 节点
|
|
355
|
+
*/
|
|
356
|
+
function placeholder(id) {
|
|
357
|
+
return {
|
|
358
|
+
type: "Placeholder",
|
|
359
|
+
id
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* 创建标识符 AST 节点
|
|
364
|
+
*/
|
|
365
|
+
function identifier(name) {
|
|
366
|
+
return {
|
|
367
|
+
type: "Identifier",
|
|
368
|
+
name
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* 创建数字字面量 AST 节点
|
|
373
|
+
*/
|
|
374
|
+
function numberLiteral(value) {
|
|
375
|
+
return {
|
|
376
|
+
type: "NumberLiteral",
|
|
377
|
+
value,
|
|
378
|
+
raw: String(value)
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 创建字符串字面量 AST 节点
|
|
383
|
+
*/
|
|
384
|
+
function stringLiteral(value, quote = "\"") {
|
|
385
|
+
return {
|
|
386
|
+
type: "StringLiteral",
|
|
387
|
+
value,
|
|
388
|
+
quote
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 创建成员表达式 AST 节点
|
|
393
|
+
*/
|
|
394
|
+
function memberExpr(object, property) {
|
|
395
|
+
return {
|
|
396
|
+
type: "MemberExpr",
|
|
397
|
+
object,
|
|
398
|
+
property,
|
|
399
|
+
computed: false,
|
|
400
|
+
optional: false
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* 创建调用表达式 AST 节点
|
|
405
|
+
*/
|
|
406
|
+
function callExpr(callee, arguments_) {
|
|
407
|
+
return {
|
|
408
|
+
type: "CallExpr",
|
|
409
|
+
callee,
|
|
410
|
+
arguments: arguments_,
|
|
411
|
+
optional: false
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* 创建数组表达式 AST 节点
|
|
416
|
+
*/
|
|
417
|
+
function arrayExpr(elements) {
|
|
418
|
+
return {
|
|
419
|
+
type: "ArrayExpr",
|
|
420
|
+
elements
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 检查对象是否为 TypedArray 实例
|
|
425
|
+
*/
|
|
426
|
+
function getTypedArrayConstructor(value) {
|
|
427
|
+
for (const constructorName of typedArrayConstructors) {
|
|
428
|
+
const Constructor = globalThis[constructorName];
|
|
429
|
+
if (Constructor && value instanceof Constructor) return Constructor;
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 序列化参数为 AST 节点
|
|
435
|
+
* - Proxy Variable/Expression:使用 ast 或占位符标识符
|
|
436
|
+
* - 数组:返回 ArrayExpr 节点
|
|
437
|
+
* - 对象:返回 ObjectExpr 节点
|
|
438
|
+
* - 原始值:返回对应的字面量节点
|
|
439
|
+
* - Date, RegExp, BigInt, URL, URLSearchParams, Map, Set, TypedArray, DataView: 构造函数调用
|
|
440
|
+
*/
|
|
441
|
+
function serializeArgumentToAST(arg) {
|
|
442
|
+
if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
443
|
+
const meta = getProxyMetadata(arg);
|
|
444
|
+
if (meta) {
|
|
445
|
+
if (meta.ast) return meta.ast;
|
|
446
|
+
if (meta.rootVariable) return placeholder(meta.rootVariable);
|
|
186
447
|
}
|
|
187
|
-
return node;
|
|
188
448
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (ch === "(") {
|
|
197
|
-
const arrowFunc = this.tryParseArrowFunction();
|
|
198
|
-
if (arrowFunc) return arrowFunc;
|
|
199
|
-
this.advance();
|
|
200
|
-
this.skipWhitespace();
|
|
201
|
-
const expr = this.parseExpression();
|
|
202
|
-
this.skipWhitespace();
|
|
203
|
-
this.expect(")");
|
|
204
|
-
return expr;
|
|
449
|
+
if (Array.isArray(arg)) return arrayExpr(arg.map(serializeArgumentToAST));
|
|
450
|
+
if (typeof arg === "object" && arg !== null) {
|
|
451
|
+
if (arg instanceof Date) return callExpr(identifier("Date"), [numberLiteral(arg.getTime())]);
|
|
452
|
+
if (arg instanceof RegExp) {
|
|
453
|
+
const args = [stringLiteral(arg.source)];
|
|
454
|
+
if (arg.flags) args.push(stringLiteral(arg.flags));
|
|
455
|
+
return callExpr(identifier("RegExp"), args);
|
|
205
456
|
}
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
};
|
|
214
|
-
if (this.matchKeyword("null")) return { type: "NullLiteral" };
|
|
215
|
-
if (this.matchKeyword("undefined")) return {
|
|
216
|
-
type: "Identifier",
|
|
217
|
-
name: "undefined"
|
|
218
|
-
};
|
|
219
|
-
if (this.isIdentifierStart(ch)) {
|
|
220
|
-
const arrowFunc = this.tryParseSingleParamArrowFunction();
|
|
221
|
-
if (arrowFunc) return arrowFunc;
|
|
222
|
-
return this.parseIdentifier();
|
|
457
|
+
if (typeof URL !== "undefined" && arg instanceof URL) return callExpr(identifier("URL"), [stringLiteral(arg.href)]);
|
|
458
|
+
if (typeof URLSearchParams !== "undefined" && arg instanceof URLSearchParams) {
|
|
459
|
+
const entries = [];
|
|
460
|
+
arg.forEach((value, key) => {
|
|
461
|
+
entries.push(arrayExpr([stringLiteral(key), stringLiteral(value)]));
|
|
462
|
+
});
|
|
463
|
+
return callExpr(identifier("URLSearchParams"), [arrayExpr(entries)]);
|
|
223
464
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (next === "x" || next === "o" || next === "b") {
|
|
231
|
-
this.advance();
|
|
232
|
-
this.advance();
|
|
233
|
-
while (this.isHexDigit(this.peek())) this.advance();
|
|
234
|
-
const raw = this.source.slice(start, this.pos);
|
|
235
|
-
return {
|
|
236
|
-
type: "NumberLiteral",
|
|
237
|
-
value: Number(raw),
|
|
238
|
-
raw
|
|
239
|
-
};
|
|
240
|
-
}
|
|
465
|
+
if (arg instanceof Map) {
|
|
466
|
+
const entries = [];
|
|
467
|
+
arg.forEach((value, key) => {
|
|
468
|
+
entries.push(arrayExpr([serializeArgumentToAST(key), serializeArgumentToAST(value)]));
|
|
469
|
+
});
|
|
470
|
+
return callExpr(identifier("Map"), [arrayExpr(entries)]);
|
|
241
471
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
472
|
+
if (arg instanceof Set) {
|
|
473
|
+
const values = [];
|
|
474
|
+
arg.forEach((value) => values.push(serializeArgumentToAST(value)));
|
|
475
|
+
return callExpr(identifier("Set"), [arrayExpr(values)]);
|
|
246
476
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
477
|
+
const typedArrayConstructor = getTypedArrayConstructor(arg);
|
|
478
|
+
if (typedArrayConstructor) {
|
|
479
|
+
const values = [...arg].map(serializeArgumentToAST);
|
|
480
|
+
const constructorName = typedArrayConstructor.name;
|
|
481
|
+
return callExpr(identifier(constructorName), [arrayExpr(values)]);
|
|
251
482
|
}
|
|
252
|
-
|
|
483
|
+
if (arg instanceof ArrayBuffer) {
|
|
484
|
+
const uint8Array = new Uint8Array(arg);
|
|
485
|
+
const values = Array.from(uint8Array).map(numberLiteral);
|
|
486
|
+
return memberExpr(callExpr(identifier("Uint8Array"), [arrayExpr(values)]), identifier("buffer"));
|
|
487
|
+
}
|
|
488
|
+
if (arg instanceof DataView) return callExpr(identifier("DataView"), [serializeArgumentToAST(arg.buffer)]);
|
|
253
489
|
return {
|
|
254
|
-
type: "
|
|
255
|
-
|
|
256
|
-
|
|
490
|
+
type: "ObjectExpr",
|
|
491
|
+
properties: Object.entries(arg).map(([k, v]) => {
|
|
492
|
+
return {
|
|
493
|
+
key: /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? identifier(k) : stringLiteral(k),
|
|
494
|
+
value: serializeArgumentToAST(v),
|
|
495
|
+
computed: false,
|
|
496
|
+
shorthand: false
|
|
497
|
+
};
|
|
498
|
+
})
|
|
257
499
|
};
|
|
258
500
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"'": "'",
|
|
265
|
-
"\"": "\"",
|
|
266
|
-
"`": "`"
|
|
501
|
+
if (arg === null) return { type: "NullLiteral" };
|
|
502
|
+
if (arg === void 0) return identifier("undefined");
|
|
503
|
+
if (typeof arg === "boolean") return {
|
|
504
|
+
type: "BooleanLiteral",
|
|
505
|
+
value: arg
|
|
267
506
|
};
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
type: "StringLiteral",
|
|
284
|
-
value,
|
|
285
|
-
quote
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
parseArray() {
|
|
289
|
-
this.expect("[");
|
|
290
|
-
const elements = [];
|
|
291
|
-
this.skipWhitespace();
|
|
292
|
-
while (this.peek() !== "]") {
|
|
293
|
-
elements.push(this.parseExpression());
|
|
294
|
-
this.skipWhitespace();
|
|
295
|
-
if (this.peek() === ",") {
|
|
296
|
-
this.advance();
|
|
297
|
-
this.skipWhitespace();
|
|
298
|
-
} else break;
|
|
299
|
-
}
|
|
300
|
-
this.expect("]");
|
|
301
|
-
return {
|
|
302
|
-
type: "ArrayExpr",
|
|
303
|
-
elements
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
parseObject() {
|
|
307
|
-
this.expect("{");
|
|
308
|
-
const properties = [];
|
|
309
|
-
this.skipWhitespace();
|
|
310
|
-
while (this.peek() !== "}") {
|
|
311
|
-
const prop = this.parseObjectProperty();
|
|
312
|
-
properties.push(prop);
|
|
313
|
-
this.skipWhitespace();
|
|
314
|
-
if (this.peek() === ",") {
|
|
315
|
-
this.advance();
|
|
316
|
-
this.skipWhitespace();
|
|
317
|
-
} else break;
|
|
318
|
-
}
|
|
319
|
-
this.expect("}");
|
|
320
|
-
return {
|
|
321
|
-
type: "ObjectExpr",
|
|
322
|
-
properties
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
parseObjectProperty() {
|
|
326
|
-
this.skipWhitespace();
|
|
327
|
-
let key;
|
|
328
|
-
let computed = false;
|
|
329
|
-
if (this.peek() === "[") {
|
|
330
|
-
this.advance();
|
|
331
|
-
this.skipWhitespace();
|
|
332
|
-
key = this.parseExpression();
|
|
333
|
-
this.skipWhitespace();
|
|
334
|
-
this.expect("]");
|
|
335
|
-
computed = true;
|
|
336
|
-
} else if (this.peek() === "\"" || this.peek() === "'") key = this.parseString();
|
|
337
|
-
else key = this.parseIdentifier();
|
|
338
|
-
this.skipWhitespace();
|
|
339
|
-
if (this.peek() === ":") {
|
|
340
|
-
this.advance();
|
|
341
|
-
this.skipWhitespace();
|
|
342
|
-
const value = this.parseExpression();
|
|
343
|
-
return {
|
|
344
|
-
key,
|
|
345
|
-
value,
|
|
346
|
-
computed,
|
|
347
|
-
shorthand: false
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
if (key.type !== "Identifier") throw new Error("Shorthand property must be an identifier");
|
|
351
|
-
return {
|
|
352
|
-
key,
|
|
353
|
-
value: key,
|
|
354
|
-
computed: false,
|
|
355
|
-
shorthand: true
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
parseIdentifier() {
|
|
359
|
-
const start = this.pos;
|
|
360
|
-
while (this.isIdentifierPart(this.peek())) this.advance();
|
|
361
|
-
const name = this.source.slice(start, this.pos);
|
|
362
|
-
if (!name) throw new Error(`Expected identifier at position ${this.pos}`);
|
|
363
|
-
return {
|
|
364
|
-
type: "Identifier",
|
|
365
|
-
name
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* 尝试解析带括号的箭头函数: (a, b) => expr
|
|
370
|
-
* 使用回溯机制
|
|
371
|
-
*/
|
|
372
|
-
tryParseArrowFunction() {
|
|
373
|
-
const savedPos = this.pos;
|
|
374
|
-
try {
|
|
375
|
-
this.expect("(");
|
|
376
|
-
this.skipWhitespace();
|
|
377
|
-
const params = [];
|
|
378
|
-
while (this.peek() !== ")") {
|
|
379
|
-
if (!this.isIdentifierStart(this.peek())) throw new Error("Expected identifier");
|
|
380
|
-
params.push(this.parseIdentifier());
|
|
381
|
-
this.skipWhitespace();
|
|
382
|
-
if (this.peek() === ",") {
|
|
383
|
-
this.advance();
|
|
384
|
-
this.skipWhitespace();
|
|
385
|
-
} else break;
|
|
386
|
-
}
|
|
387
|
-
this.expect(")");
|
|
388
|
-
this.skipWhitespace();
|
|
389
|
-
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
390
|
-
this.pos += 2;
|
|
391
|
-
this.skipWhitespace();
|
|
392
|
-
return {
|
|
393
|
-
type: "ArrowFunctionExpr",
|
|
394
|
-
params,
|
|
395
|
-
body: this.parseExpression()
|
|
396
|
-
};
|
|
397
|
-
} catch {
|
|
398
|
-
this.pos = savedPos;
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* 尝试解析单参数无括号的箭头函数: a => expr
|
|
404
|
-
* 使用回溯机制
|
|
405
|
-
*/
|
|
406
|
-
tryParseSingleParamArrowFunction() {
|
|
407
|
-
const savedPos = this.pos;
|
|
408
|
-
try {
|
|
409
|
-
const param = this.parseIdentifier();
|
|
410
|
-
this.skipWhitespace();
|
|
411
|
-
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
412
|
-
this.pos += 2;
|
|
413
|
-
this.skipWhitespace();
|
|
414
|
-
const body = this.parseExpression();
|
|
415
|
-
return {
|
|
416
|
-
type: "ArrowFunctionExpr",
|
|
417
|
-
params: [param],
|
|
418
|
-
body
|
|
419
|
-
};
|
|
420
|
-
} catch {
|
|
421
|
-
this.pos = savedPos;
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
parseArguments() {
|
|
426
|
-
const args = [];
|
|
427
|
-
this.skipWhitespace();
|
|
428
|
-
while (this.peek() !== ")") {
|
|
429
|
-
args.push(this.parseExpression());
|
|
430
|
-
this.skipWhitespace();
|
|
431
|
-
if (this.peek() === ",") {
|
|
432
|
-
this.advance();
|
|
433
|
-
this.skipWhitespace();
|
|
434
|
-
} else break;
|
|
435
|
-
}
|
|
436
|
-
return args;
|
|
437
|
-
}
|
|
438
|
-
static OPERATORS = [
|
|
439
|
-
"instanceof",
|
|
440
|
-
">>>",
|
|
441
|
-
"===",
|
|
442
|
-
"!==",
|
|
443
|
-
"&&",
|
|
444
|
-
"||",
|
|
445
|
-
"??",
|
|
446
|
-
"==",
|
|
447
|
-
"!=",
|
|
448
|
-
"<=",
|
|
449
|
-
">=",
|
|
450
|
-
"<<",
|
|
451
|
-
">>",
|
|
452
|
-
"**",
|
|
453
|
-
"in",
|
|
454
|
-
"+",
|
|
455
|
-
"-",
|
|
456
|
-
"*",
|
|
457
|
-
"/",
|
|
458
|
-
"%",
|
|
459
|
-
"<",
|
|
460
|
-
">",
|
|
461
|
-
"&",
|
|
462
|
-
"|",
|
|
463
|
-
"^"
|
|
464
|
-
];
|
|
465
|
-
static KEYWORD_OPERATORS = new Set(["in", "instanceof"]);
|
|
466
|
-
peekOperator() {
|
|
467
|
-
for (const op of Parser.OPERATORS) {
|
|
468
|
-
if (!this.source.startsWith(op, this.pos)) continue;
|
|
469
|
-
if (Parser.KEYWORD_OPERATORS.has(op)) {
|
|
470
|
-
const nextChar = this.source[this.pos + op.length];
|
|
471
|
-
if (nextChar && this.isIdentifierPart(nextChar)) continue;
|
|
472
|
-
}
|
|
473
|
-
return op;
|
|
474
|
-
}
|
|
475
|
-
return null;
|
|
507
|
+
if (typeof arg === "number") return numberLiteral(arg);
|
|
508
|
+
if (typeof arg === "string") return stringLiteral(arg);
|
|
509
|
+
if (typeof arg === "bigint") return callExpr(identifier("BigInt"), [stringLiteral(arg.toString())]);
|
|
510
|
+
throw new Error(`Unsupported argument type: ${typeof arg}`);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* 从参数中收集依赖的 Symbol
|
|
514
|
+
* 递归遍历数组和对象,收集所有 Proxy 的依赖
|
|
515
|
+
*/
|
|
516
|
+
function collectDepsFromArgs(args, deps) {
|
|
517
|
+
for (const arg of args) if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
518
|
+
const meta = getProxyMetadata(arg);
|
|
519
|
+
if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
|
|
520
|
+
else if (Array.isArray(arg)) collectDepsFromArgs(arg, deps);
|
|
521
|
+
else if (typeof arg === "object") collectDepsFromArgs(Object.values(arg), deps);
|
|
476
522
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* 根据路径构建成员表达式 AST
|
|
526
|
+
*/
|
|
527
|
+
function buildMemberExprAst(rootId, path) {
|
|
528
|
+
let ast = placeholder(rootId);
|
|
529
|
+
for (const prop of path) ast = memberExpr(ast, identifier(prop));
|
|
530
|
+
return ast;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* 创建 Proxy 的公共 handler
|
|
534
|
+
*/
|
|
535
|
+
function createProxyHandler(ast, deps) {
|
|
536
|
+
return {
|
|
537
|
+
get(_target, prop) {
|
|
538
|
+
if (typeof prop === "symbol") return void 0;
|
|
539
|
+
return createProxyExpressionWithAST(memberExpr(ast, identifier(String(prop))), deps);
|
|
540
|
+
},
|
|
541
|
+
apply(_target, _thisArg, args) {
|
|
542
|
+
const callAst = callExpr(ast, args.map(serializeArgumentToAST));
|
|
543
|
+
const newDeps = new Set(deps);
|
|
544
|
+
collectDepsFromArgs(args, newDeps);
|
|
545
|
+
return createProxyExpressionWithAST(callAst, newDeps);
|
|
484
546
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
isIdentifierPart(ch) {
|
|
516
|
-
const code = ch.charCodeAt(0);
|
|
517
|
-
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 95 || code === 36;
|
|
518
|
-
}
|
|
519
|
-
};
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* 创建根 Variable Proxy
|
|
551
|
+
* 拦截属性访问,返回新的 expression proxy
|
|
552
|
+
* 不可直接调用(apply 应该只在链式调用后可用)
|
|
553
|
+
*
|
|
554
|
+
* @param id - 变量的唯一标识 Symbol
|
|
555
|
+
* @returns Proxy 包装的 Variable
|
|
556
|
+
*/
|
|
557
|
+
function createProxyVariable(id) {
|
|
558
|
+
const deps = new Set([id]);
|
|
559
|
+
const proxy = new Proxy(function() {}, {
|
|
560
|
+
get(_target, prop) {
|
|
561
|
+
if (typeof prop === "symbol") return void 0;
|
|
562
|
+
return createProxyExpressionWithAST(buildMemberExprAst(id, [String(prop)]), deps);
|
|
563
|
+
},
|
|
564
|
+
apply() {
|
|
565
|
+
throw new Error("Variable cannot be called directly");
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
setProxyMetadata(proxy, {
|
|
569
|
+
type: "variable",
|
|
570
|
+
path: [],
|
|
571
|
+
rootVariable: id,
|
|
572
|
+
dependencies: deps
|
|
573
|
+
});
|
|
574
|
+
return proxy;
|
|
575
|
+
}
|
|
520
576
|
/**
|
|
521
|
-
*
|
|
577
|
+
* 创建带完整 AST 的 Proxy(方法调用后)
|
|
578
|
+
* 可以继续链式访问和调用
|
|
579
|
+
*
|
|
580
|
+
* @param ast - 完整的表达式 AST
|
|
581
|
+
* @param deps - 依赖集合
|
|
582
|
+
* @returns Proxy 包装的 Expression
|
|
522
583
|
*/
|
|
523
|
-
function
|
|
524
|
-
|
|
584
|
+
function createProxyExpressionWithAST(ast, deps) {
|
|
585
|
+
const proxy = new Proxy(function() {}, createProxyHandler(ast, deps));
|
|
586
|
+
setProxyMetadata(proxy, {
|
|
587
|
+
type: "expression",
|
|
588
|
+
path: [],
|
|
589
|
+
ast,
|
|
590
|
+
dependencies: deps
|
|
591
|
+
});
|
|
592
|
+
return proxy;
|
|
525
593
|
}
|
|
594
|
+
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/variable.ts
|
|
526
597
|
/**
|
|
527
|
-
*
|
|
598
|
+
* 跟踪每个 variable 的唯一 Symbol ID
|
|
528
599
|
*/
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
case "MemberExpr": {
|
|
551
|
-
const object = wrapIfNeeded(node.object, node, "object");
|
|
552
|
-
if (node.computed) {
|
|
553
|
-
const property = generate(node.property);
|
|
554
|
-
return node.optional ? `${object}?.[${property}]` : `${object}[${property}]`;
|
|
555
|
-
}
|
|
556
|
-
const property = generate(node.property);
|
|
557
|
-
return node.optional ? `${object}?.${property}` : `${object}.${property}`;
|
|
558
|
-
}
|
|
559
|
-
case "CallExpr": {
|
|
560
|
-
const callee = wrapIfNeeded(node.callee, node, "callee");
|
|
561
|
-
const args = node.arguments.map(generate).join(",");
|
|
562
|
-
if (node.callee.type === "Identifier" && [
|
|
563
|
-
"Date",
|
|
564
|
-
"RegExp",
|
|
565
|
-
"URL",
|
|
566
|
-
"URLSearchParams",
|
|
567
|
-
"Map",
|
|
568
|
-
"Set",
|
|
569
|
-
"Int8Array",
|
|
570
|
-
"Uint8Array",
|
|
571
|
-
"Uint8ClampedArray",
|
|
572
|
-
"Int16Array",
|
|
573
|
-
"Uint16Array",
|
|
574
|
-
"Int32Array",
|
|
575
|
-
"Uint32Array",
|
|
576
|
-
"Float32Array",
|
|
577
|
-
"Float64Array",
|
|
578
|
-
"BigInt64Array",
|
|
579
|
-
"BigUint64Array",
|
|
580
|
-
"ArrayBuffer",
|
|
581
|
-
"DataView"
|
|
582
|
-
].includes(node.callee.name)) return node.optional ? `new ${callee}?.(${args})` : `new ${callee}(${args})`;
|
|
583
|
-
return node.optional ? `${callee}?.(${args})` : `${callee}(${args})`;
|
|
584
|
-
}
|
|
585
|
-
case "ArrayExpr": return `[${node.elements.map(generate).join(",")}]`;
|
|
586
|
-
case "ObjectExpr": return `{${node.properties.map((prop) => {
|
|
587
|
-
if (prop.shorthand) return generate(prop.key);
|
|
588
|
-
return `${prop.computed ? `[${generate(prop.key)}]` : generate(prop.key)}:${generate(prop.value)}`;
|
|
589
|
-
}).join(",")}}`;
|
|
590
|
-
case "ArrowFunctionExpr": {
|
|
591
|
-
const params = node.params.map((p) => p.name).join(",");
|
|
592
|
-
let body = generate(node.body);
|
|
593
|
-
if (node.body.type === "ObjectExpr") body = `(${body})`;
|
|
594
|
-
if (node.params.length === 1) return `${params}=>${body}`;
|
|
595
|
-
return `(${params})=>${body}`;
|
|
596
|
-
}
|
|
597
|
-
default: {
|
|
598
|
-
const nodeType = node.type ?? "unknown";
|
|
599
|
-
throw new Error(`Unknown node type: ${nodeType}`);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
600
|
+
const variableIds = /* @__PURE__ */ new WeakMap();
|
|
601
|
+
/**
|
|
602
|
+
* 计数器用于生成唯一变量 ID
|
|
603
|
+
*/
|
|
604
|
+
let variableCounter = 0;
|
|
605
|
+
/**
|
|
606
|
+
* 创建一个类型化变量
|
|
607
|
+
* 返回 Proxy 对象,支持链式属性访问和方法调用
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* ```ts
|
|
611
|
+
* const x = variable<number>();
|
|
612
|
+
* const config = variable<{ timeout: number }>();
|
|
613
|
+
* const timeout = config.timeout; // Proxy expression
|
|
614
|
+
* ```
|
|
615
|
+
*/
|
|
616
|
+
function variable() {
|
|
617
|
+
const id = Symbol(`var_${variableCounter++}`);
|
|
618
|
+
const proxy = createProxyVariable(id);
|
|
619
|
+
variableIds.set(proxy, id);
|
|
620
|
+
return proxy;
|
|
602
621
|
}
|
|
603
622
|
/**
|
|
604
|
-
*
|
|
623
|
+
* 获取 variable 的唯一 Symbol ID
|
|
605
624
|
*/
|
|
606
|
-
function
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return code;
|
|
625
|
+
function getVariableId(variable) {
|
|
626
|
+
if (typeof variable !== "object" && typeof variable !== "function" || variable === null) return void 0;
|
|
627
|
+
return variableIds.get(variable);
|
|
610
628
|
}
|
|
629
|
+
|
|
630
|
+
//#endregion
|
|
631
|
+
//#region src/compile.ts
|
|
632
|
+
const ALLOWED_GLOBALS = new Set([
|
|
633
|
+
"Math",
|
|
634
|
+
"JSON",
|
|
635
|
+
"Date",
|
|
636
|
+
"RegExp",
|
|
637
|
+
"Number",
|
|
638
|
+
"String",
|
|
639
|
+
"Boolean",
|
|
640
|
+
"Array",
|
|
641
|
+
"Object",
|
|
642
|
+
"undefined",
|
|
643
|
+
"NaN",
|
|
644
|
+
"Infinity",
|
|
645
|
+
"isNaN",
|
|
646
|
+
"isFinite",
|
|
647
|
+
"parseInt",
|
|
648
|
+
"parseFloat",
|
|
649
|
+
"BigInt",
|
|
650
|
+
"URL",
|
|
651
|
+
"URLSearchParams",
|
|
652
|
+
"Map",
|
|
653
|
+
"Set",
|
|
654
|
+
"Int8Array",
|
|
655
|
+
"Uint8Array",
|
|
656
|
+
"Uint8ClampedArray",
|
|
657
|
+
"Int16Array",
|
|
658
|
+
"Uint16Array",
|
|
659
|
+
"Int32Array",
|
|
660
|
+
"Uint32Array",
|
|
661
|
+
"Float32Array",
|
|
662
|
+
"Float64Array",
|
|
663
|
+
"BigInt64Array",
|
|
664
|
+
"BigUint64Array",
|
|
665
|
+
"ArrayBuffer",
|
|
666
|
+
"DataView"
|
|
667
|
+
]);
|
|
611
668
|
/**
|
|
612
|
-
*
|
|
669
|
+
* 将 Proxy Expression 编译为可序列化的 JSON 结构
|
|
670
|
+
*
|
|
671
|
+
* @template TResult - 表达式结果类型
|
|
672
|
+
* @param expression - Proxy Expression,或包含 Proxy 的对象/数组/原始值
|
|
673
|
+
* @param variables - 所有使用的变量定义
|
|
674
|
+
* @param options - 编译选项
|
|
675
|
+
* @returns 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
676
|
+
*
|
|
677
|
+
* @throws 如果传入无效的表达式或未定义的变量引用
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```ts
|
|
681
|
+
* const x = variable<number>()
|
|
682
|
+
* const y = variable<number>()
|
|
683
|
+
* const sum = expr({ x, y })("x + y")
|
|
684
|
+
* const result = expr({ sum, x })("sum * x")
|
|
685
|
+
* const compiled = compile(result, { x, y })
|
|
686
|
+
* // => [["x", "y"], "($0+$1)*$0"]
|
|
687
|
+
* ```
|
|
613
688
|
*/
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
689
|
+
function compile(expression, variables, options = {}) {
|
|
690
|
+
const { shortCircuit = true } = options;
|
|
691
|
+
const ast = serializeArgumentToAST(expression);
|
|
692
|
+
const variableOrder = [];
|
|
693
|
+
const variableToIndex = /* @__PURE__ */ new Map();
|
|
694
|
+
const symbolToName = /* @__PURE__ */ new Map();
|
|
695
|
+
for (const [name, value] of Object.entries(variables)) {
|
|
696
|
+
if (!variableToIndex.has(name)) {
|
|
697
|
+
variableToIndex.set(name, variableOrder.length);
|
|
698
|
+
variableOrder.push(name);
|
|
622
699
|
}
|
|
700
|
+
const id = getVariableId(value);
|
|
701
|
+
if (id) symbolToName.set(id, name);
|
|
623
702
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (
|
|
627
|
-
|
|
703
|
+
const placeholderTransformed = transformPlaceholders(ast, (id) => {
|
|
704
|
+
const name = symbolToName.get(id);
|
|
705
|
+
if (!name) return null;
|
|
706
|
+
const index = variableToIndex.get(name);
|
|
707
|
+
if (index === void 0) return null;
|
|
708
|
+
return `$${index}`;
|
|
709
|
+
});
|
|
710
|
+
const undefinedVars = [];
|
|
711
|
+
const transformed = transformIdentifiers(placeholderTransformed, (name) => {
|
|
712
|
+
if (name.startsWith("$") && /^\$\d+$/.test(name)) return name;
|
|
713
|
+
const index = variableToIndex.get(name);
|
|
714
|
+
if (index !== void 0) return `$${index}`;
|
|
715
|
+
if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
|
|
716
|
+
return name;
|
|
717
|
+
});
|
|
718
|
+
if (undefinedVars.length > 0) {
|
|
719
|
+
const uniqueVars = [...new Set(undefinedVars)];
|
|
720
|
+
throw new Error(`Undefined variable(s): ${uniqueVars.join(", ")}`);
|
|
628
721
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
switch (node.type) {
|
|
639
|
-
case "Identifier": {
|
|
640
|
-
const result = transform(node.name);
|
|
641
|
-
return typeof result === "string" ? {
|
|
642
|
-
...node,
|
|
643
|
-
name: result
|
|
644
|
-
} : result;
|
|
722
|
+
const expressions = [];
|
|
723
|
+
if (shortCircuit) {
|
|
724
|
+
let nextIndex = variableOrder.length;
|
|
725
|
+
function compileAst(node) {
|
|
726
|
+
if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node);
|
|
727
|
+
if (node.type === "ConditionalExpr") return compileConditional(node);
|
|
728
|
+
const exprStr = generate(node);
|
|
729
|
+
expressions.push(exprStr);
|
|
730
|
+
return nextIndex++;
|
|
645
731
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
...node,
|
|
653
|
-
argument: transformIdentifiers(node.argument, transform)
|
|
654
|
-
};
|
|
655
|
-
case "ConditionalExpr": return {
|
|
656
|
-
...node,
|
|
657
|
-
test: transformIdentifiers(node.test, transform),
|
|
658
|
-
consequent: transformIdentifiers(node.consequent, transform),
|
|
659
|
-
alternate: transformIdentifiers(node.alternate, transform)
|
|
660
|
-
};
|
|
661
|
-
case "MemberExpr": return {
|
|
662
|
-
...node,
|
|
663
|
-
object: transformIdentifiers(node.object, transform),
|
|
664
|
-
property: node.computed ? transformIdentifiers(node.property, transform) : node.property
|
|
665
|
-
};
|
|
666
|
-
case "CallExpr": return {
|
|
667
|
-
...node,
|
|
668
|
-
callee: transformIdentifiers(node.callee, transform),
|
|
669
|
-
arguments: node.arguments.map((arg) => transformIdentifiers(arg, transform))
|
|
670
|
-
};
|
|
671
|
-
case "ArrayExpr": return {
|
|
672
|
-
...node,
|
|
673
|
-
elements: node.elements.map((el) => transformIdentifiers(el, transform))
|
|
674
|
-
};
|
|
675
|
-
case "ObjectExpr": return {
|
|
676
|
-
...node,
|
|
677
|
-
properties: node.properties.map((prop) => ({
|
|
678
|
-
...prop,
|
|
679
|
-
key: prop.computed ? transformIdentifiers(prop.key, transform) : prop.key,
|
|
680
|
-
value: transformIdentifiers(prop.value, transform)
|
|
681
|
-
}))
|
|
682
|
-
};
|
|
683
|
-
case "ArrowFunctionExpr": {
|
|
684
|
-
const paramNames = new Set(node.params.map((p) => p.name));
|
|
685
|
-
return {
|
|
686
|
-
...node,
|
|
687
|
-
body: transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name))
|
|
732
|
+
function compileShortCircuit(node) {
|
|
733
|
+
const leftIdx = compileAst(node.left);
|
|
734
|
+
const branchConditions = {
|
|
735
|
+
"||": `$${leftIdx}`,
|
|
736
|
+
"&&": `!$${leftIdx}`,
|
|
737
|
+
"??": `$${leftIdx}!=null`
|
|
688
738
|
};
|
|
739
|
+
const branchIdx = expressions.length;
|
|
740
|
+
expressions.push([
|
|
741
|
+
"br",
|
|
742
|
+
branchConditions[node.operator],
|
|
743
|
+
0
|
|
744
|
+
]);
|
|
745
|
+
nextIndex++;
|
|
746
|
+
compileAst(node.right);
|
|
747
|
+
const skipCount = expressions.length - branchIdx - 1;
|
|
748
|
+
expressions[branchIdx][2] = skipCount;
|
|
749
|
+
const phiIdx = nextIndex++;
|
|
750
|
+
expressions.push(["phi"]);
|
|
751
|
+
return phiIdx;
|
|
752
|
+
}
|
|
753
|
+
function compileConditional(node) {
|
|
754
|
+
const testIdx = compileAst(node.test);
|
|
755
|
+
const branchIdx = expressions.length;
|
|
756
|
+
expressions.push([
|
|
757
|
+
"br",
|
|
758
|
+
`$${testIdx}`,
|
|
759
|
+
0
|
|
760
|
+
]);
|
|
761
|
+
nextIndex++;
|
|
762
|
+
compileAst(node.alternate);
|
|
763
|
+
const jmpIdx = expressions.length;
|
|
764
|
+
expressions.push(["jmp", 0]);
|
|
765
|
+
nextIndex++;
|
|
766
|
+
compileAst(node.consequent);
|
|
767
|
+
const thenEndIdx = expressions.length;
|
|
768
|
+
expressions[branchIdx][2] = jmpIdx - branchIdx;
|
|
769
|
+
expressions[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
|
|
770
|
+
const phiIdx = nextIndex++;
|
|
771
|
+
expressions.push(["phi"]);
|
|
772
|
+
return phiIdx;
|
|
689
773
|
}
|
|
690
|
-
|
|
691
|
-
}
|
|
774
|
+
compileAst(transformed);
|
|
775
|
+
} else expressions.push(generate(transformed));
|
|
776
|
+
return [variableOrder, ...expressions];
|
|
692
777
|
}
|
|
693
778
|
|
|
694
779
|
//#endregion
|
|
695
|
-
//#region src/
|
|
696
|
-
/**
|
|
697
|
-
* 全局 WeakMap 存储
|
|
698
|
-
*/
|
|
699
|
-
const proxyMetadata = /* @__PURE__ */ new WeakMap();
|
|
780
|
+
//#region src/evaluate.ts
|
|
700
781
|
/**
|
|
701
|
-
*
|
|
782
|
+
* 缓存已构造的求值函数,以提升重复执行性能
|
|
702
783
|
*/
|
|
703
|
-
|
|
704
|
-
proxyMetadata.set(proxy, metadata);
|
|
705
|
-
}
|
|
784
|
+
const evaluatorCache = /* @__PURE__ */ new Map();
|
|
706
785
|
/**
|
|
707
|
-
*
|
|
786
|
+
* 检测编译数据是否包含控制流节点(V2 格式)
|
|
708
787
|
*/
|
|
709
|
-
function
|
|
710
|
-
return
|
|
788
|
+
function isV2Format(expressions) {
|
|
789
|
+
return expressions.some((expr) => Array.isArray(expr));
|
|
711
790
|
}
|
|
712
791
|
/**
|
|
713
|
-
*
|
|
792
|
+
* 执行编译后的表达式
|
|
793
|
+
*
|
|
794
|
+
* @template TResult - 表达式结果类型
|
|
795
|
+
* @param data - 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
796
|
+
* @param values - 变量值映射,按变量名提供值
|
|
797
|
+
* @returns 最后一个表达式的求值结果
|
|
798
|
+
*
|
|
799
|
+
* @throws 如果运行时类型验证失败或表达式执行出错
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```ts
|
|
803
|
+
* const compiled = [["x", "y"], "$0+$1", "$1*2"]
|
|
804
|
+
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
|
|
805
|
+
* // => 6 (3 * 2)
|
|
806
|
+
* ```
|
|
714
807
|
*/
|
|
715
|
-
function
|
|
716
|
-
if (
|
|
717
|
-
|
|
808
|
+
function evaluate(data, values) {
|
|
809
|
+
if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
|
|
810
|
+
const [variableNames, ...expressions] = data;
|
|
811
|
+
if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
|
|
812
|
+
for (const varName of variableNames) {
|
|
813
|
+
if (typeof varName !== "string") throw new Error("Invalid compiled data: variable names must be strings");
|
|
814
|
+
if (!(varName in values)) throw new Error(`Missing required variable: ${varName}`);
|
|
815
|
+
}
|
|
816
|
+
const valueArray = [];
|
|
817
|
+
for (const varName of variableNames) valueArray.push(values[varName]);
|
|
818
|
+
const cacheKey = JSON.stringify(data);
|
|
819
|
+
let evaluator = evaluatorCache.get(cacheKey);
|
|
820
|
+
if (!evaluator) {
|
|
821
|
+
const functionBody = isV2Format(expressions) ? buildEvaluatorFunctionBodyV2(expressions, variableNames.length) : buildEvaluatorFunctionBody(expressions, variableNames.length);
|
|
822
|
+
evaluator = new Function("$values", functionBody);
|
|
823
|
+
evaluatorCache.set(cacheKey, evaluator);
|
|
824
|
+
}
|
|
825
|
+
try {
|
|
826
|
+
return evaluator(valueArray);
|
|
827
|
+
} catch (error) {
|
|
828
|
+
throw new Error(`Failed to evaluate expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
829
|
+
}
|
|
718
830
|
}
|
|
719
831
|
/**
|
|
720
|
-
*
|
|
832
|
+
* 构造求值函数体
|
|
833
|
+
*
|
|
834
|
+
* @param expressions - 表达式列表
|
|
835
|
+
* @param variableCount - 变量数量
|
|
836
|
+
* @returns 函数体字符串
|
|
837
|
+
*
|
|
838
|
+
* @example
|
|
839
|
+
* ```ts
|
|
840
|
+
* buildEvaluatorFunctionBody(["$0+$1", "$2*2"], 2)
|
|
841
|
+
* // 返回执行 $0+$1 并存储到 $values[2],然后执行 $2*2 的函数体
|
|
842
|
+
* ```
|
|
721
843
|
*/
|
|
722
|
-
function
|
|
723
|
-
if (
|
|
724
|
-
return
|
|
844
|
+
function buildEvaluatorFunctionBody(expressions, variableCount) {
|
|
845
|
+
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
846
|
+
return [
|
|
847
|
+
...Array.from({ length: variableCount }, (_, i) => `const $${i} = $values[${i}];`),
|
|
848
|
+
...expressions.map((expr, i) => {
|
|
849
|
+
const idx = variableCount + i;
|
|
850
|
+
return `const $${idx} = ${expr}; $values[${idx}] = $${idx};`;
|
|
851
|
+
}),
|
|
852
|
+
`return $values[$values.length - 1];`
|
|
853
|
+
].join("\n");
|
|
725
854
|
}
|
|
726
855
|
/**
|
|
727
|
-
*
|
|
856
|
+
* 构造带控制流支持的求值函数体(V2 格式)
|
|
857
|
+
*
|
|
858
|
+
* @param expressions - 表达式列表(可包含控制流节点)
|
|
859
|
+
* @param variableCount - 变量数量
|
|
860
|
+
* @returns 函数体字符串
|
|
728
861
|
*/
|
|
729
|
-
function
|
|
730
|
-
if (
|
|
731
|
-
|
|
862
|
+
function buildEvaluatorFunctionBodyV2(expressions, variableCount) {
|
|
863
|
+
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
864
|
+
const lines = [
|
|
865
|
+
...Array.from({ length: variableCount }, (_, i) => `const $${i} = $values[${i}];`),
|
|
866
|
+
"let $pc = 0;",
|
|
867
|
+
"let $lastValue;",
|
|
868
|
+
...expressions.map((_, i) => `let $${variableCount + i};`),
|
|
869
|
+
`while ($pc < ${expressions.length}) {`,
|
|
870
|
+
" switch ($pc) {"
|
|
871
|
+
];
|
|
872
|
+
expressions.forEach((expr, i) => {
|
|
873
|
+
const idx = variableCount + i;
|
|
874
|
+
lines.push(` case ${i}: {`);
|
|
875
|
+
if (typeof expr === "string") {
|
|
876
|
+
lines.push(` $${idx} = $lastValue = ${expr};`);
|
|
877
|
+
lines.push(` $values[${idx}] = $${idx};`);
|
|
878
|
+
lines.push(" $pc++; break;");
|
|
879
|
+
} else {
|
|
880
|
+
const [type] = expr;
|
|
881
|
+
switch (type) {
|
|
882
|
+
case "br":
|
|
883
|
+
lines.push(` if (${expr[1]}) { $pc += ${expr[2] + 1}; } else { $pc++; } break;`);
|
|
884
|
+
break;
|
|
885
|
+
case "jmp":
|
|
886
|
+
lines.push(` $pc += ${expr[1] + 1}; break;`);
|
|
887
|
+
break;
|
|
888
|
+
case "phi":
|
|
889
|
+
lines.push(` $${idx} = $values[${idx}] = $lastValue; $pc++; break;`);
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
lines.push(" }");
|
|
894
|
+
});
|
|
895
|
+
lines.push(" }", "}", "return $values[$values.length - 1];");
|
|
896
|
+
return lines.join("\n");
|
|
732
897
|
}
|
|
733
898
|
|
|
734
899
|
//#endregion
|
|
735
|
-
//#region src/
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
900
|
+
//#region src/parser.ts
|
|
901
|
+
var Parser = class Parser {
|
|
902
|
+
pos = 0;
|
|
903
|
+
source;
|
|
904
|
+
constructor(source) {
|
|
905
|
+
this.source = source;
|
|
906
|
+
}
|
|
907
|
+
parse() {
|
|
908
|
+
this.skipWhitespace();
|
|
909
|
+
const node = this.parseExpression();
|
|
910
|
+
this.skipWhitespace();
|
|
911
|
+
if (this.pos < this.source.length) throw new Error(`Unexpected token at position ${this.pos}: ${this.source.slice(this.pos, this.pos + 10)}`);
|
|
912
|
+
return node;
|
|
913
|
+
}
|
|
914
|
+
parseExpression() {
|
|
915
|
+
return this.parseConditional();
|
|
916
|
+
}
|
|
917
|
+
parseConditional() {
|
|
918
|
+
let node = this.parseBinary(0);
|
|
919
|
+
this.skipWhitespace();
|
|
920
|
+
if (this.peek() === "?") {
|
|
921
|
+
this.advance();
|
|
922
|
+
this.skipWhitespace();
|
|
923
|
+
const consequent = this.parseExpression();
|
|
924
|
+
this.skipWhitespace();
|
|
925
|
+
this.expect(":");
|
|
926
|
+
this.skipWhitespace();
|
|
927
|
+
const alternate = this.parseExpression();
|
|
928
|
+
node = {
|
|
929
|
+
type: "ConditionalExpr",
|
|
930
|
+
test: node,
|
|
931
|
+
consequent,
|
|
932
|
+
alternate
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
return node;
|
|
936
|
+
}
|
|
937
|
+
parseBinary(minPrec) {
|
|
938
|
+
let left = this.parseUnary();
|
|
939
|
+
while (true) {
|
|
940
|
+
this.skipWhitespace();
|
|
941
|
+
const op = this.peekOperator();
|
|
942
|
+
if (!op || PRECEDENCE[op] === void 0 || PRECEDENCE[op] < minPrec) break;
|
|
943
|
+
this.pos += op.length;
|
|
944
|
+
this.skipWhitespace();
|
|
945
|
+
const nextMinPrec = RIGHT_ASSOCIATIVE.has(op) ? PRECEDENCE[op] : PRECEDENCE[op] + 1;
|
|
946
|
+
const right = this.parseBinary(nextMinPrec);
|
|
947
|
+
left = {
|
|
948
|
+
type: "BinaryExpr",
|
|
949
|
+
operator: op,
|
|
950
|
+
left,
|
|
951
|
+
right
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
return left;
|
|
955
|
+
}
|
|
956
|
+
parseUnary() {
|
|
957
|
+
this.skipWhitespace();
|
|
958
|
+
const ch = this.peek();
|
|
959
|
+
if (ch === "!" || ch === "~" || ch === "+" || ch === "-") {
|
|
960
|
+
this.advance();
|
|
961
|
+
this.skipWhitespace();
|
|
962
|
+
return {
|
|
963
|
+
type: "UnaryExpr",
|
|
964
|
+
operator: ch,
|
|
965
|
+
argument: this.parseUnary(),
|
|
966
|
+
prefix: true
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
for (const keyword of ["typeof", "void"]) if (this.matchKeyword(keyword)) {
|
|
970
|
+
this.skipWhitespace();
|
|
971
|
+
return {
|
|
972
|
+
type: "UnaryExpr",
|
|
973
|
+
operator: keyword,
|
|
974
|
+
argument: this.parseUnary(),
|
|
975
|
+
prefix: true
|
|
759
976
|
};
|
|
760
977
|
}
|
|
978
|
+
return this.parsePostfix();
|
|
979
|
+
}
|
|
980
|
+
parsePostfix() {
|
|
981
|
+
let node = this.parsePrimary();
|
|
982
|
+
while (true) {
|
|
983
|
+
this.skipWhitespace();
|
|
984
|
+
const ch = this.peek();
|
|
985
|
+
if (ch === ".") {
|
|
986
|
+
this.advance();
|
|
987
|
+
this.skipWhitespace();
|
|
988
|
+
const property = this.parseIdentifier();
|
|
989
|
+
node = {
|
|
990
|
+
type: "MemberExpr",
|
|
991
|
+
object: node,
|
|
992
|
+
property,
|
|
993
|
+
computed: false,
|
|
994
|
+
optional: false
|
|
995
|
+
};
|
|
996
|
+
} else if (ch === "[") {
|
|
997
|
+
this.advance();
|
|
998
|
+
this.skipWhitespace();
|
|
999
|
+
const property = this.parseExpression();
|
|
1000
|
+
this.skipWhitespace();
|
|
1001
|
+
this.expect("]");
|
|
1002
|
+
node = {
|
|
1003
|
+
type: "MemberExpr",
|
|
1004
|
+
object: node,
|
|
1005
|
+
property,
|
|
1006
|
+
computed: true,
|
|
1007
|
+
optional: false
|
|
1008
|
+
};
|
|
1009
|
+
} else if (ch === "(") {
|
|
1010
|
+
this.advance();
|
|
1011
|
+
const args = this.parseArguments();
|
|
1012
|
+
this.expect(")");
|
|
1013
|
+
node = {
|
|
1014
|
+
type: "CallExpr",
|
|
1015
|
+
callee: node,
|
|
1016
|
+
arguments: args,
|
|
1017
|
+
optional: false
|
|
1018
|
+
};
|
|
1019
|
+
} else if (ch === "?" && this.peekAt(1) === ".") {
|
|
1020
|
+
this.advance();
|
|
1021
|
+
this.advance();
|
|
1022
|
+
this.skipWhitespace();
|
|
1023
|
+
if (this.peek() === "[") {
|
|
1024
|
+
this.advance();
|
|
1025
|
+
this.skipWhitespace();
|
|
1026
|
+
const property = this.parseExpression();
|
|
1027
|
+
this.skipWhitespace();
|
|
1028
|
+
this.expect("]");
|
|
1029
|
+
node = {
|
|
1030
|
+
type: "MemberExpr",
|
|
1031
|
+
object: node,
|
|
1032
|
+
property,
|
|
1033
|
+
computed: true,
|
|
1034
|
+
optional: true
|
|
1035
|
+
};
|
|
1036
|
+
} else if (this.peek() === "(") {
|
|
1037
|
+
this.advance();
|
|
1038
|
+
const args = this.parseArguments();
|
|
1039
|
+
this.expect(")");
|
|
1040
|
+
node = {
|
|
1041
|
+
type: "CallExpr",
|
|
1042
|
+
callee: node,
|
|
1043
|
+
arguments: args,
|
|
1044
|
+
optional: true
|
|
1045
|
+
};
|
|
1046
|
+
} else {
|
|
1047
|
+
const property = this.parseIdentifier();
|
|
1048
|
+
node = {
|
|
1049
|
+
type: "MemberExpr",
|
|
1050
|
+
object: node,
|
|
1051
|
+
property,
|
|
1052
|
+
computed: false,
|
|
1053
|
+
optional: true
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
} else break;
|
|
1057
|
+
}
|
|
1058
|
+
return node;
|
|
761
1059
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
if (
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
optional: false
|
|
779
|
-
};
|
|
780
|
-
if (arg instanceof RegExp) {
|
|
781
|
-
const args = [{
|
|
782
|
-
type: "StringLiteral",
|
|
783
|
-
value: arg.source,
|
|
784
|
-
quote: "\""
|
|
785
|
-
}];
|
|
786
|
-
if (arg.flags) args.push({
|
|
787
|
-
type: "StringLiteral",
|
|
788
|
-
value: arg.flags,
|
|
789
|
-
quote: "\""
|
|
790
|
-
});
|
|
791
|
-
return {
|
|
792
|
-
type: "CallExpr",
|
|
793
|
-
callee: {
|
|
794
|
-
type: "Identifier",
|
|
795
|
-
name: "RegExp"
|
|
796
|
-
},
|
|
797
|
-
arguments: args,
|
|
798
|
-
optional: false
|
|
799
|
-
};
|
|
1060
|
+
parsePrimary() {
|
|
1061
|
+
this.skipWhitespace();
|
|
1062
|
+
const ch = this.peek();
|
|
1063
|
+
if (this.isDigit(ch) || ch === "." && this.isDigit(this.peekAt(1))) return this.parseNumber();
|
|
1064
|
+
if (ch === "\"" || ch === "'" || ch === "`") return this.parseString();
|
|
1065
|
+
if (ch === "[") return this.parseArray();
|
|
1066
|
+
if (ch === "{") return this.parseObject();
|
|
1067
|
+
if (ch === "(") {
|
|
1068
|
+
const arrowFunc = this.tryParseArrowFunction();
|
|
1069
|
+
if (arrowFunc) return arrowFunc;
|
|
1070
|
+
this.advance();
|
|
1071
|
+
this.skipWhitespace();
|
|
1072
|
+
const expr = this.parseExpression();
|
|
1073
|
+
this.skipWhitespace();
|
|
1074
|
+
this.expect(")");
|
|
1075
|
+
return expr;
|
|
800
1076
|
}
|
|
801
|
-
if (
|
|
802
|
-
type: "
|
|
803
|
-
|
|
804
|
-
type: "Identifier",
|
|
805
|
-
name: "URL"
|
|
806
|
-
},
|
|
807
|
-
arguments: [{
|
|
808
|
-
type: "StringLiteral",
|
|
809
|
-
value: arg.href,
|
|
810
|
-
quote: "\""
|
|
811
|
-
}],
|
|
812
|
-
optional: false
|
|
1077
|
+
if (this.matchKeyword("true")) return {
|
|
1078
|
+
type: "BooleanLiteral",
|
|
1079
|
+
value: true
|
|
813
1080
|
};
|
|
814
|
-
if (
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}]
|
|
828
|
-
});
|
|
829
|
-
});
|
|
830
|
-
return {
|
|
831
|
-
type: "CallExpr",
|
|
832
|
-
callee: {
|
|
833
|
-
type: "Identifier",
|
|
834
|
-
name: "URLSearchParams"
|
|
835
|
-
},
|
|
836
|
-
arguments: [{
|
|
837
|
-
type: "ArrayExpr",
|
|
838
|
-
elements: entries
|
|
839
|
-
}],
|
|
840
|
-
optional: false
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
if (arg instanceof Map) {
|
|
844
|
-
const entries = [];
|
|
845
|
-
arg.forEach((value, key) => {
|
|
846
|
-
entries.push({
|
|
847
|
-
type: "ArrayExpr",
|
|
848
|
-
elements: [serializeArgumentToAST(key), serializeArgumentToAST(value)]
|
|
849
|
-
});
|
|
850
|
-
});
|
|
851
|
-
return {
|
|
852
|
-
type: "CallExpr",
|
|
853
|
-
callee: {
|
|
854
|
-
type: "Identifier",
|
|
855
|
-
name: "Map"
|
|
856
|
-
},
|
|
857
|
-
arguments: [{
|
|
858
|
-
type: "ArrayExpr",
|
|
859
|
-
elements: entries
|
|
860
|
-
}],
|
|
861
|
-
optional: false
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
if (arg instanceof Set) {
|
|
865
|
-
const values = [];
|
|
866
|
-
arg.forEach((value) => {
|
|
867
|
-
values.push(serializeArgumentToAST(value));
|
|
868
|
-
});
|
|
869
|
-
return {
|
|
870
|
-
type: "CallExpr",
|
|
871
|
-
callee: {
|
|
872
|
-
type: "Identifier",
|
|
873
|
-
name: "Set"
|
|
874
|
-
},
|
|
875
|
-
arguments: [{
|
|
876
|
-
type: "ArrayExpr",
|
|
877
|
-
elements: values
|
|
878
|
-
}],
|
|
879
|
-
optional: false
|
|
880
|
-
};
|
|
1081
|
+
if (this.matchKeyword("false")) return {
|
|
1082
|
+
type: "BooleanLiteral",
|
|
1083
|
+
value: false
|
|
1084
|
+
};
|
|
1085
|
+
if (this.matchKeyword("null")) return { type: "NullLiteral" };
|
|
1086
|
+
if (this.matchKeyword("undefined")) return {
|
|
1087
|
+
type: "Identifier",
|
|
1088
|
+
name: "undefined"
|
|
1089
|
+
};
|
|
1090
|
+
if (this.isIdentifierStart(ch)) {
|
|
1091
|
+
const arrowFunc = this.tryParseSingleParamArrowFunction();
|
|
1092
|
+
if (arrowFunc) return arrowFunc;
|
|
1093
|
+
return this.parseIdentifier();
|
|
881
1094
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
"
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
"BigUint64Array"
|
|
894
|
-
]) if (typeof globalThis[constructorName] !== "undefined") {
|
|
895
|
-
if (arg instanceof globalThis[constructorName]) {
|
|
896
|
-
const values = [...arg].map((val) => serializeArgumentToAST(val));
|
|
1095
|
+
throw new Error(`Unexpected character at position ${this.pos}: ${ch}`);
|
|
1096
|
+
}
|
|
1097
|
+
parseNumber() {
|
|
1098
|
+
const start = this.pos;
|
|
1099
|
+
if (this.peek() === "0") {
|
|
1100
|
+
const next = this.peekAt(1)?.toLowerCase();
|
|
1101
|
+
if (next === "x" || next === "o" || next === "b") {
|
|
1102
|
+
this.advance();
|
|
1103
|
+
this.advance();
|
|
1104
|
+
while (this.isHexDigit(this.peek())) this.advance();
|
|
1105
|
+
const raw = this.source.slice(start, this.pos);
|
|
897
1106
|
return {
|
|
898
|
-
type: "
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
name: constructorName
|
|
902
|
-
},
|
|
903
|
-
arguments: [{
|
|
904
|
-
type: "ArrayExpr",
|
|
905
|
-
elements: values
|
|
906
|
-
}],
|
|
907
|
-
optional: false
|
|
1107
|
+
type: "NumberLiteral",
|
|
1108
|
+
value: Number(raw),
|
|
1109
|
+
raw
|
|
908
1110
|
};
|
|
909
1111
|
}
|
|
910
1112
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
object: {
|
|
916
|
-
type: "CallExpr",
|
|
917
|
-
callee: {
|
|
918
|
-
type: "Identifier",
|
|
919
|
-
name: "Uint8Array"
|
|
920
|
-
},
|
|
921
|
-
arguments: [{
|
|
922
|
-
type: "ArrayExpr",
|
|
923
|
-
elements: Array.from(uint8Array).map((val) => serializeArgumentToAST(val))
|
|
924
|
-
}],
|
|
925
|
-
optional: false
|
|
926
|
-
},
|
|
927
|
-
property: {
|
|
928
|
-
type: "Identifier",
|
|
929
|
-
name: "buffer"
|
|
930
|
-
},
|
|
931
|
-
computed: false,
|
|
932
|
-
optional: false
|
|
933
|
-
};
|
|
1113
|
+
while (this.isDigit(this.peek())) this.advance();
|
|
1114
|
+
if (this.peek() === "." && this.isDigit(this.peekAt(1))) {
|
|
1115
|
+
this.advance();
|
|
1116
|
+
while (this.isDigit(this.peek())) this.advance();
|
|
934
1117
|
}
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1118
|
+
if (this.peek()?.toLowerCase() === "e") {
|
|
1119
|
+
this.advance();
|
|
1120
|
+
if (this.peek() === "+" || this.peek() === "-") this.advance();
|
|
1121
|
+
while (this.isDigit(this.peek())) this.advance();
|
|
1122
|
+
}
|
|
1123
|
+
const raw = this.source.slice(start, this.pos);
|
|
1124
|
+
return {
|
|
1125
|
+
type: "NumberLiteral",
|
|
1126
|
+
value: Number(raw),
|
|
1127
|
+
raw
|
|
943
1128
|
};
|
|
944
1129
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
type: "StringLiteral",
|
|
954
|
-
value: k,
|
|
955
|
-
quote: "\""
|
|
956
|
-
},
|
|
957
|
-
value: serializeArgumentToAST(v),
|
|
958
|
-
computed: false,
|
|
959
|
-
shorthand: false
|
|
960
|
-
};
|
|
961
|
-
})
|
|
962
|
-
};
|
|
963
|
-
if (arg === null) return { type: "NullLiteral" };
|
|
964
|
-
if (arg === void 0) return {
|
|
965
|
-
type: "Identifier",
|
|
966
|
-
name: "undefined"
|
|
967
|
-
};
|
|
968
|
-
if (typeof arg === "boolean") return {
|
|
969
|
-
type: "BooleanLiteral",
|
|
970
|
-
value: arg
|
|
971
|
-
};
|
|
972
|
-
if (typeof arg === "number") return {
|
|
973
|
-
type: "NumberLiteral",
|
|
974
|
-
value: arg,
|
|
975
|
-
raw: String(arg)
|
|
976
|
-
};
|
|
977
|
-
if (typeof arg === "string") return {
|
|
978
|
-
type: "StringLiteral",
|
|
979
|
-
value: arg,
|
|
980
|
-
quote: "\""
|
|
1130
|
+
static ESCAPE_CHARS = {
|
|
1131
|
+
n: "\n",
|
|
1132
|
+
r: "\r",
|
|
1133
|
+
t: " ",
|
|
1134
|
+
"\\": "\\",
|
|
1135
|
+
"'": "'",
|
|
1136
|
+
"\"": "\"",
|
|
1137
|
+
"`": "`"
|
|
981
1138
|
};
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1139
|
+
parseString() {
|
|
1140
|
+
const quote = this.peek();
|
|
1141
|
+
this.advance();
|
|
1142
|
+
let value = "";
|
|
1143
|
+
while (this.pos < this.source.length && this.peek() !== quote) if (this.peek() === "\\") {
|
|
1144
|
+
this.advance();
|
|
1145
|
+
const escaped = this.peek();
|
|
1146
|
+
value += Parser.ESCAPE_CHARS[escaped] ?? escaped;
|
|
1147
|
+
this.advance();
|
|
1148
|
+
} else {
|
|
1149
|
+
value += this.peek();
|
|
1150
|
+
this.advance();
|
|
1151
|
+
}
|
|
1152
|
+
this.expect(quote);
|
|
1153
|
+
return {
|
|
989
1154
|
type: "StringLiteral",
|
|
990
|
-
value
|
|
991
|
-
quote
|
|
992
|
-
}
|
|
993
|
-
optional: false
|
|
994
|
-
};
|
|
995
|
-
throw new Error(`Unsupported argument type: ${typeof arg}`);
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* 从参数中收集依赖的 Symbol
|
|
999
|
-
* 递归遍历数组和对象,收集所有 Proxy 的依赖
|
|
1000
|
-
*/
|
|
1001
|
-
function collectDepsFromArgs(args, deps) {
|
|
1002
|
-
for (const arg of args) if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
1003
|
-
const meta = getProxyMetadata(arg);
|
|
1004
|
-
if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
|
|
1005
|
-
else if (Array.isArray(arg)) collectDepsFromArgs(arg, deps);
|
|
1006
|
-
else if (typeof arg === "object") collectDepsFromArgs(Object.values(arg), deps);
|
|
1155
|
+
value,
|
|
1156
|
+
quote
|
|
1157
|
+
};
|
|
1007
1158
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const proxy = new Proxy(function() {}, {
|
|
1020
|
-
get(_target, prop) {
|
|
1021
|
-
if (typeof prop === "symbol") return void 0;
|
|
1022
|
-
return createProxyExpression(id, [String(prop)], deps);
|
|
1023
|
-
},
|
|
1024
|
-
apply() {
|
|
1025
|
-
throw new Error("Variable cannot be called directly");
|
|
1159
|
+
parseArray() {
|
|
1160
|
+
this.expect("[");
|
|
1161
|
+
const elements = [];
|
|
1162
|
+
this.skipWhitespace();
|
|
1163
|
+
while (this.peek() !== "]") {
|
|
1164
|
+
elements.push(this.parseExpression());
|
|
1165
|
+
this.skipWhitespace();
|
|
1166
|
+
if (this.peek() === ",") {
|
|
1167
|
+
this.advance();
|
|
1168
|
+
this.skipWhitespace();
|
|
1169
|
+
} else break;
|
|
1026
1170
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
*/
|
|
1046
|
-
function createProxyExpression(rootId, path, deps) {
|
|
1047
|
-
let ast = {
|
|
1048
|
-
type: "Identifier",
|
|
1049
|
-
name: getVariablePlaceholder$1(rootId)
|
|
1050
|
-
};
|
|
1051
|
-
for (const prop of path) ast = {
|
|
1052
|
-
type: "MemberExpr",
|
|
1053
|
-
object: ast,
|
|
1054
|
-
property: {
|
|
1055
|
-
type: "Identifier",
|
|
1056
|
-
name: prop
|
|
1057
|
-
},
|
|
1058
|
-
computed: false,
|
|
1059
|
-
optional: false
|
|
1060
|
-
};
|
|
1061
|
-
const proxy = new Proxy(function() {}, {
|
|
1062
|
-
get(_target, prop) {
|
|
1063
|
-
if (typeof prop === "symbol") return void 0;
|
|
1064
|
-
return createProxyExpression(rootId, [...path, String(prop)], deps);
|
|
1065
|
-
},
|
|
1066
|
-
apply(_target, _thisArg, args) {
|
|
1067
|
-
const callAst = {
|
|
1068
|
-
type: "CallExpr",
|
|
1069
|
-
callee: ast,
|
|
1070
|
-
arguments: args.map(serializeArgumentToAST),
|
|
1071
|
-
optional: false
|
|
1072
|
-
};
|
|
1073
|
-
const newDeps = new Set(deps);
|
|
1074
|
-
collectDepsFromArgs(args, newDeps);
|
|
1075
|
-
return createProxyExpressionWithAST(callAst, newDeps);
|
|
1171
|
+
this.expect("]");
|
|
1172
|
+
return {
|
|
1173
|
+
type: "ArrayExpr",
|
|
1174
|
+
elements
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
parseObject() {
|
|
1178
|
+
this.expect("{");
|
|
1179
|
+
const properties = [];
|
|
1180
|
+
this.skipWhitespace();
|
|
1181
|
+
while (this.peek() !== "}") {
|
|
1182
|
+
const prop = this.parseObjectProperty();
|
|
1183
|
+
properties.push(prop);
|
|
1184
|
+
this.skipWhitespace();
|
|
1185
|
+
if (this.peek() === ",") {
|
|
1186
|
+
this.advance();
|
|
1187
|
+
this.skipWhitespace();
|
|
1188
|
+
} else break;
|
|
1076
1189
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
computed: false,
|
|
1107
|
-
optional: false
|
|
1108
|
-
}, deps);
|
|
1109
|
-
},
|
|
1110
|
-
apply(_target, _thisArg, args) {
|
|
1111
|
-
const callAst = {
|
|
1112
|
-
type: "CallExpr",
|
|
1113
|
-
callee: ast,
|
|
1114
|
-
arguments: args.map(serializeArgumentToAST),
|
|
1115
|
-
optional: false
|
|
1190
|
+
this.expect("}");
|
|
1191
|
+
return {
|
|
1192
|
+
type: "ObjectExpr",
|
|
1193
|
+
properties
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
parseObjectProperty() {
|
|
1197
|
+
this.skipWhitespace();
|
|
1198
|
+
let key;
|
|
1199
|
+
let computed = false;
|
|
1200
|
+
if (this.peek() === "[") {
|
|
1201
|
+
this.advance();
|
|
1202
|
+
this.skipWhitespace();
|
|
1203
|
+
key = this.parseExpression();
|
|
1204
|
+
this.skipWhitespace();
|
|
1205
|
+
this.expect("]");
|
|
1206
|
+
computed = true;
|
|
1207
|
+
} else if (this.peek() === "\"" || this.peek() === "'") key = this.parseString();
|
|
1208
|
+
else key = this.parseIdentifier();
|
|
1209
|
+
this.skipWhitespace();
|
|
1210
|
+
if (this.peek() === ":") {
|
|
1211
|
+
this.advance();
|
|
1212
|
+
this.skipWhitespace();
|
|
1213
|
+
const value = this.parseExpression();
|
|
1214
|
+
return {
|
|
1215
|
+
key,
|
|
1216
|
+
value,
|
|
1217
|
+
computed,
|
|
1218
|
+
shorthand: false
|
|
1116
1219
|
};
|
|
1117
|
-
const newDeps = new Set(deps);
|
|
1118
|
-
collectDepsFromArgs(args, newDeps);
|
|
1119
|
-
return createProxyExpressionWithAST(callAst, newDeps);
|
|
1120
1220
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
return proxy;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
//#endregion
|
|
1132
|
-
//#region src/variable.ts
|
|
1133
|
-
/**
|
|
1134
|
-
* 跟踪每个 variable 的唯一 Symbol ID
|
|
1135
|
-
*/
|
|
1136
|
-
const variableIds = /* @__PURE__ */ new WeakMap();
|
|
1137
|
-
/**
|
|
1138
|
-
* 计数器用于生成唯一变量 ID
|
|
1139
|
-
*/
|
|
1140
|
-
let variableCounter = 0;
|
|
1141
|
-
/**
|
|
1142
|
-
* 创建一个类型化变量
|
|
1143
|
-
* 返回 Proxy 对象,支持链式属性访问和方法调用
|
|
1144
|
-
*
|
|
1145
|
-
* @example
|
|
1146
|
-
* ```ts
|
|
1147
|
-
* const x = variable<number>();
|
|
1148
|
-
* const config = variable<{ timeout: number }>();
|
|
1149
|
-
* const timeout = config.timeout; // Proxy expression
|
|
1150
|
-
* ```
|
|
1151
|
-
*/
|
|
1152
|
-
function variable() {
|
|
1153
|
-
const id = Symbol(`var_${variableCounter++}`);
|
|
1154
|
-
const proxy = createProxyVariable(id);
|
|
1155
|
-
variableIds.set(proxy, id);
|
|
1156
|
-
return proxy;
|
|
1157
|
-
}
|
|
1158
|
-
/**
|
|
1159
|
-
* 获取 variable 的唯一 Symbol ID
|
|
1160
|
-
*/
|
|
1161
|
-
function getVariableId(variable) {
|
|
1162
|
-
if (typeof variable !== "object" && typeof variable !== "function" || variable === null) return void 0;
|
|
1163
|
-
return variableIds.get(variable);
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* 生成变量占位符字符串
|
|
1167
|
-
* 格式:$$VAR_var_N$$
|
|
1168
|
-
*/
|
|
1169
|
-
function getVariablePlaceholder(id) {
|
|
1170
|
-
return `$$VAR_${id.description}$$`;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
//#endregion
|
|
1174
|
-
//#region src/compile.ts
|
|
1175
|
-
const ALLOWED_GLOBALS = new Set([
|
|
1176
|
-
"Math",
|
|
1177
|
-
"JSON",
|
|
1178
|
-
"Date",
|
|
1179
|
-
"RegExp",
|
|
1180
|
-
"Number",
|
|
1181
|
-
"String",
|
|
1182
|
-
"Boolean",
|
|
1183
|
-
"Array",
|
|
1184
|
-
"Object",
|
|
1185
|
-
"undefined",
|
|
1186
|
-
"NaN",
|
|
1187
|
-
"Infinity",
|
|
1188
|
-
"isNaN",
|
|
1189
|
-
"isFinite",
|
|
1190
|
-
"parseInt",
|
|
1191
|
-
"parseFloat",
|
|
1192
|
-
"BigInt",
|
|
1193
|
-
"URL",
|
|
1194
|
-
"URLSearchParams",
|
|
1195
|
-
"Map",
|
|
1196
|
-
"Set",
|
|
1197
|
-
"Int8Array",
|
|
1198
|
-
"Uint8Array",
|
|
1199
|
-
"Uint8ClampedArray",
|
|
1200
|
-
"Int16Array",
|
|
1201
|
-
"Uint16Array",
|
|
1202
|
-
"Int32Array",
|
|
1203
|
-
"Uint32Array",
|
|
1204
|
-
"Float32Array",
|
|
1205
|
-
"Float64Array",
|
|
1206
|
-
"BigInt64Array",
|
|
1207
|
-
"BigUint64Array",
|
|
1208
|
-
"ArrayBuffer",
|
|
1209
|
-
"DataView"
|
|
1210
|
-
]);
|
|
1211
|
-
/**
|
|
1212
|
-
* 将 Proxy Expression 编译为可序列化的 JSON 结构
|
|
1213
|
-
*
|
|
1214
|
-
* @template TResult - 表达式结果类型
|
|
1215
|
-
* @param expression - Proxy Expression,或包含 Proxy 的对象/数组/原始值
|
|
1216
|
-
* @param variables - 所有使用的变量定义
|
|
1217
|
-
* @param options - 编译选项
|
|
1218
|
-
* @returns 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
1219
|
-
*
|
|
1220
|
-
* @throws 如果传入无效的表达式或未定义的变量引用
|
|
1221
|
-
*
|
|
1222
|
-
* @example
|
|
1223
|
-
* ```ts
|
|
1224
|
-
* const x = variable<number>()
|
|
1225
|
-
* const y = variable<number>()
|
|
1226
|
-
* const sum = expr({ x, y })("x + y")
|
|
1227
|
-
* const result = expr({ sum, x })("sum * x")
|
|
1228
|
-
* const compiled = compile(result, { x, y })
|
|
1229
|
-
* // => [["x", "y"], "($0+$1)*$0"]
|
|
1230
|
-
* ```
|
|
1231
|
-
*/
|
|
1232
|
-
function compile(expression, variables, options = {}) {
|
|
1233
|
-
const { shortCircuit = true } = options;
|
|
1234
|
-
const ast = serializeArgumentToAST(expression);
|
|
1235
|
-
const variableOrder = [];
|
|
1236
|
-
const variableToIndex = /* @__PURE__ */ new Map();
|
|
1237
|
-
for (const name of Object.keys(variables)) if (!variableToIndex.has(name)) {
|
|
1238
|
-
variableToIndex.set(name, variableOrder.length);
|
|
1239
|
-
variableOrder.push(name);
|
|
1221
|
+
if (key.type !== "Identifier") throw new Error("Shorthand property must be an identifier");
|
|
1222
|
+
return {
|
|
1223
|
+
key,
|
|
1224
|
+
value: key,
|
|
1225
|
+
computed: false,
|
|
1226
|
+
shorthand: true
|
|
1227
|
+
};
|
|
1240
1228
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1229
|
+
parseIdentifier() {
|
|
1230
|
+
const start = this.pos;
|
|
1231
|
+
while (this.isIdentifierPart(this.peek())) this.advance();
|
|
1232
|
+
const name = this.source.slice(start, this.pos);
|
|
1233
|
+
if (!name) throw new Error(`Expected identifier at position ${this.pos}`);
|
|
1234
|
+
return {
|
|
1235
|
+
type: "Identifier",
|
|
1236
|
+
name
|
|
1237
|
+
};
|
|
1245
1238
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1239
|
+
/**
|
|
1240
|
+
* 尝试解析带括号的箭头函数: (a, b) => expr
|
|
1241
|
+
* 使用回溯机制
|
|
1242
|
+
*/
|
|
1243
|
+
tryParseArrowFunction() {
|
|
1244
|
+
const savedPos = this.pos;
|
|
1245
|
+
try {
|
|
1246
|
+
this.expect("(");
|
|
1247
|
+
this.skipWhitespace();
|
|
1248
|
+
const params = [];
|
|
1249
|
+
while (this.peek() !== ")") {
|
|
1250
|
+
if (!this.isIdentifierStart(this.peek())) throw new Error("Expected identifier");
|
|
1251
|
+
params.push(this.parseIdentifier());
|
|
1252
|
+
this.skipWhitespace();
|
|
1253
|
+
if (this.peek() === ",") {
|
|
1254
|
+
this.advance();
|
|
1255
|
+
this.skipWhitespace();
|
|
1256
|
+
} else break;
|
|
1257
|
+
}
|
|
1258
|
+
this.expect(")");
|
|
1259
|
+
this.skipWhitespace();
|
|
1260
|
+
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
1261
|
+
this.pos += 2;
|
|
1262
|
+
this.skipWhitespace();
|
|
1263
|
+
return {
|
|
1264
|
+
type: "ArrowFunctionExpr",
|
|
1265
|
+
params,
|
|
1266
|
+
body: this.parseExpression()
|
|
1267
|
+
};
|
|
1268
|
+
} catch {
|
|
1269
|
+
this.pos = savedPos;
|
|
1270
|
+
return null;
|
|
1254
1271
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
if (
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1270
|
-
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* 尝试解析单参数无括号的箭头函数: a => expr
|
|
1275
|
+
* 使用回溯机制
|
|
1276
|
+
*/
|
|
1277
|
+
tryParseSingleParamArrowFunction() {
|
|
1278
|
+
const savedPos = this.pos;
|
|
1279
|
+
try {
|
|
1280
|
+
const param = this.parseIdentifier();
|
|
1281
|
+
this.skipWhitespace();
|
|
1282
|
+
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
1283
|
+
this.pos += 2;
|
|
1284
|
+
this.skipWhitespace();
|
|
1285
|
+
const body = this.parseExpression();
|
|
1286
|
+
return {
|
|
1287
|
+
type: "ArrowFunctionExpr",
|
|
1288
|
+
params: [param],
|
|
1289
|
+
body
|
|
1290
|
+
};
|
|
1291
|
+
} catch {
|
|
1292
|
+
this.pos = savedPos;
|
|
1293
|
+
return null;
|
|
1271
1294
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1295
|
+
}
|
|
1296
|
+
parseArguments() {
|
|
1297
|
+
const args = [];
|
|
1298
|
+
this.skipWhitespace();
|
|
1299
|
+
while (this.peek() !== ")") {
|
|
1300
|
+
args.push(this.parseExpression());
|
|
1301
|
+
this.skipWhitespace();
|
|
1302
|
+
if (this.peek() === ",") {
|
|
1303
|
+
this.advance();
|
|
1304
|
+
this.skipWhitespace();
|
|
1305
|
+
} else break;
|
|
1306
|
+
}
|
|
1307
|
+
return args;
|
|
1308
|
+
}
|
|
1309
|
+
static OPERATORS = [
|
|
1310
|
+
"instanceof",
|
|
1311
|
+
">>>",
|
|
1312
|
+
"===",
|
|
1313
|
+
"!==",
|
|
1314
|
+
"&&",
|
|
1315
|
+
"||",
|
|
1316
|
+
"??",
|
|
1317
|
+
"==",
|
|
1318
|
+
"!=",
|
|
1319
|
+
"<=",
|
|
1320
|
+
">=",
|
|
1321
|
+
"<<",
|
|
1322
|
+
">>",
|
|
1323
|
+
"**",
|
|
1324
|
+
"in",
|
|
1325
|
+
"+",
|
|
1326
|
+
"-",
|
|
1327
|
+
"*",
|
|
1328
|
+
"/",
|
|
1329
|
+
"%",
|
|
1330
|
+
"<",
|
|
1331
|
+
">",
|
|
1332
|
+
"&",
|
|
1333
|
+
"|",
|
|
1334
|
+
"^"
|
|
1335
|
+
];
|
|
1336
|
+
static KEYWORD_OPERATORS = new Set(["in", "instanceof"]);
|
|
1337
|
+
peekOperator() {
|
|
1338
|
+
for (const op of Parser.OPERATORS) {
|
|
1339
|
+
if (!this.source.startsWith(op, this.pos)) continue;
|
|
1340
|
+
if (Parser.KEYWORD_OPERATORS.has(op)) {
|
|
1341
|
+
const nextChar = this.source[this.pos + op.length];
|
|
1342
|
+
if (nextChar && this.isIdentifierPart(nextChar)) continue;
|
|
1283
1343
|
}
|
|
1284
|
-
|
|
1285
|
-
expressions.push([
|
|
1286
|
-
"br",
|
|
1287
|
-
branchCondition,
|
|
1288
|
-
0
|
|
1289
|
-
]);
|
|
1290
|
-
nextIndex++;
|
|
1291
|
-
compileAst(node.right);
|
|
1292
|
-
const skipCount = expressions.length - branchIdx - 1;
|
|
1293
|
-
expressions[branchIdx][2] = skipCount;
|
|
1294
|
-
const phiIdx = nextIndex++;
|
|
1295
|
-
expressions.push(["phi"]);
|
|
1296
|
-
return phiIdx;
|
|
1344
|
+
return op;
|
|
1297
1345
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
compileAst(node.alternate);
|
|
1308
|
-
const jmpIdx = expressions.length;
|
|
1309
|
-
expressions.push(["jmp", 0]);
|
|
1310
|
-
nextIndex++;
|
|
1311
|
-
compileAst(node.consequent);
|
|
1312
|
-
const thenEndIdx = expressions.length;
|
|
1313
|
-
expressions[branchIdx][2] = jmpIdx - branchIdx;
|
|
1314
|
-
expressions[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
|
|
1315
|
-
const phiIdx = nextIndex++;
|
|
1316
|
-
expressions.push(["phi"]);
|
|
1317
|
-
return phiIdx;
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
matchKeyword(keyword) {
|
|
1349
|
+
if (this.source.startsWith(keyword, this.pos)) {
|
|
1350
|
+
const nextChar = this.source[this.pos + keyword.length];
|
|
1351
|
+
if (!nextChar || !this.isIdentifierPart(nextChar)) {
|
|
1352
|
+
this.pos += keyword.length;
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1318
1355
|
}
|
|
1319
|
-
|
|
1320
|
-
} else expressions.push(generate(transformed));
|
|
1321
|
-
return [variableOrder, ...expressions];
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
//#endregion
|
|
1325
|
-
//#region src/evaluate.ts
|
|
1326
|
-
/**
|
|
1327
|
-
* 缓存已构造的求值函数,以提升重复执行性能
|
|
1328
|
-
*/
|
|
1329
|
-
const evaluatorCache = /* @__PURE__ */ new Map();
|
|
1330
|
-
/**
|
|
1331
|
-
* 检测编译数据是否包含控制流节点(V2 格式)
|
|
1332
|
-
*/
|
|
1333
|
-
function isV2Format(expressions) {
|
|
1334
|
-
return expressions.some((expr) => Array.isArray(expr));
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* 执行编译后的表达式
|
|
1338
|
-
*
|
|
1339
|
-
* @template TResult - 表达式结果类型
|
|
1340
|
-
* @param data - 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
1341
|
-
* @param values - 变量值映射,按变量名提供值
|
|
1342
|
-
* @returns 最后一个表达式的求值结果
|
|
1343
|
-
*
|
|
1344
|
-
* @throws 如果运行时类型验证失败或表达式执行出错
|
|
1345
|
-
*
|
|
1346
|
-
* @example
|
|
1347
|
-
* ```ts
|
|
1348
|
-
* const compiled = [["x", "y"], "$0+$1", "$1*2"]
|
|
1349
|
-
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
|
|
1350
|
-
* // => 6 (3 * 2)
|
|
1351
|
-
* ```
|
|
1352
|
-
*/
|
|
1353
|
-
function evaluate(data, values) {
|
|
1354
|
-
if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
|
|
1355
|
-
const [variableNames, ...expressions] = data;
|
|
1356
|
-
if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
|
|
1357
|
-
for (const varName of variableNames) {
|
|
1358
|
-
if (typeof varName !== "string") throw new Error("Invalid compiled data: variable names must be strings");
|
|
1359
|
-
if (!(varName in values)) throw new Error(`Missing required variable: ${varName}`);
|
|
1356
|
+
return false;
|
|
1360
1357
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const cacheKey = JSON.stringify(data);
|
|
1364
|
-
let evaluator = evaluatorCache.get(cacheKey);
|
|
1365
|
-
if (!evaluator) {
|
|
1366
|
-
const functionBody = isV2Format(expressions) ? buildEvaluatorFunctionBodyV2(expressions, variableNames.length) : buildEvaluatorFunctionBody(expressions, variableNames.length);
|
|
1367
|
-
evaluator = new Function("$values", functionBody);
|
|
1368
|
-
evaluatorCache.set(cacheKey, evaluator);
|
|
1358
|
+
peek() {
|
|
1359
|
+
return this.source[this.pos] || "";
|
|
1369
1360
|
}
|
|
1370
|
-
|
|
1371
|
-
return
|
|
1372
|
-
} catch (error) {
|
|
1373
|
-
throw new Error(`Failed to evaluate expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
1361
|
+
peekAt(offset) {
|
|
1362
|
+
return this.source[this.pos + offset] || "";
|
|
1374
1363
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
* 构造求值函数体
|
|
1378
|
-
*
|
|
1379
|
-
* @param expressions - 表达式列表
|
|
1380
|
-
* @param variableCount - 变量数量
|
|
1381
|
-
* @returns 函数体字符串
|
|
1382
|
-
*
|
|
1383
|
-
* @example
|
|
1384
|
-
* ```ts
|
|
1385
|
-
* buildEvaluatorFunctionBody(["$0+$1", "$2*2"], 2)
|
|
1386
|
-
* // 返回执行 $0+$1 并存储到 $values[2],然后执行 $2*2 的函数体
|
|
1387
|
-
* ```
|
|
1388
|
-
*/
|
|
1389
|
-
function buildEvaluatorFunctionBody(expressions, variableCount) {
|
|
1390
|
-
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
1391
|
-
const lines = [];
|
|
1392
|
-
for (let i = 0; i < variableCount; i++) lines.push(`const $${i} = $values[${i}];`);
|
|
1393
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
1394
|
-
const exprSource = expressions[i];
|
|
1395
|
-
const resultIndex = variableCount + i;
|
|
1396
|
-
lines.push(`const $${resultIndex} = ${exprSource};`);
|
|
1397
|
-
lines.push(`$values[${resultIndex}] = $${resultIndex};`);
|
|
1364
|
+
advance() {
|
|
1365
|
+
return this.source[this.pos++] || "";
|
|
1398
1366
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1367
|
+
expect(ch) {
|
|
1368
|
+
if (this.peek() !== ch) throw new Error(`Expected '${ch}' at position ${this.pos}, got '${this.peek()}'`);
|
|
1369
|
+
this.advance();
|
|
1370
|
+
}
|
|
1371
|
+
skipWhitespace() {
|
|
1372
|
+
while (/\s/.test(this.peek())) this.advance();
|
|
1373
|
+
}
|
|
1374
|
+
isDigit(ch) {
|
|
1375
|
+
const code = ch.charCodeAt(0);
|
|
1376
|
+
return code >= 48 && code <= 57;
|
|
1377
|
+
}
|
|
1378
|
+
isHexDigit(ch) {
|
|
1379
|
+
const code = ch.charCodeAt(0);
|
|
1380
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
|
1381
|
+
}
|
|
1382
|
+
isIdentifierStart(ch) {
|
|
1383
|
+
const code = ch.charCodeAt(0);
|
|
1384
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95 || code === 36;
|
|
1385
|
+
}
|
|
1386
|
+
isIdentifierPart(ch) {
|
|
1387
|
+
const code = ch.charCodeAt(0);
|
|
1388
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 95 || code === 36;
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1402
1391
|
/**
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1405
|
-
* @param expressions - 表达式列表(可包含控制流节点)
|
|
1406
|
-
* @param variableCount - 变量数量
|
|
1407
|
-
* @returns 函数体字符串
|
|
1392
|
+
* 解析 JavaScript 表达式为 AST
|
|
1408
1393
|
*/
|
|
1409
|
-
function
|
|
1410
|
-
|
|
1411
|
-
const lines = [];
|
|
1412
|
-
for (let i = 0; i < variableCount; i++) lines.push(`const $${i} = $values[${i}];`);
|
|
1413
|
-
lines.push(`let $pc = 0;`);
|
|
1414
|
-
lines.push(`let $lastValue;`);
|
|
1415
|
-
for (let i = 0; i < expressions.length; i++) lines.push(`let $${variableCount + i};`);
|
|
1416
|
-
lines.push(`while ($pc < ${expressions.length}) {`);
|
|
1417
|
-
lines.push(` switch ($pc) {`);
|
|
1418
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
1419
|
-
const expr = expressions[i];
|
|
1420
|
-
const idx = variableCount + i;
|
|
1421
|
-
lines.push(` case ${i}: {`);
|
|
1422
|
-
if (typeof expr === "string") {
|
|
1423
|
-
lines.push(` $${idx} = ${expr};`);
|
|
1424
|
-
lines.push(` $values[${idx}] = $${idx};`);
|
|
1425
|
-
lines.push(` $lastValue = $${idx};`);
|
|
1426
|
-
lines.push(` $pc++; break;`);
|
|
1427
|
-
} else if (expr[0] === "br") {
|
|
1428
|
-
const [, condExpr, offset] = expr;
|
|
1429
|
-
lines.push(` if (${condExpr}) { $pc += ${offset + 1}; } else { $pc++; }`);
|
|
1430
|
-
lines.push(` break;`);
|
|
1431
|
-
} else if (expr[0] === "jmp") {
|
|
1432
|
-
const [, offset] = expr;
|
|
1433
|
-
lines.push(` $pc += ${offset + 1}; break;`);
|
|
1434
|
-
} else if (expr[0] === "phi") {
|
|
1435
|
-
lines.push(` $${idx} = $lastValue;`);
|
|
1436
|
-
lines.push(` $values[${idx}] = $lastValue;`);
|
|
1437
|
-
lines.push(` $pc++; break;`);
|
|
1438
|
-
}
|
|
1439
|
-
lines.push(` }`);
|
|
1440
|
-
}
|
|
1441
|
-
lines.push(` }`);
|
|
1442
|
-
lines.push(`}`);
|
|
1443
|
-
lines.push(`return $values[$values.length - 1];`);
|
|
1444
|
-
return lines.join("\n");
|
|
1394
|
+
function parse(source) {
|
|
1395
|
+
return new Parser(source).parse();
|
|
1445
1396
|
}
|
|
1446
1397
|
|
|
1447
1398
|
//#endregion
|
|
@@ -1499,7 +1450,10 @@ function expr(context) {
|
|
|
1499
1450
|
}
|
|
1500
1451
|
return createProxyExpressionWithAST(transformIdentifiers(parse(source), (name) => {
|
|
1501
1452
|
const id = nameToId.get(name);
|
|
1502
|
-
if (id) return
|
|
1453
|
+
if (id) return {
|
|
1454
|
+
type: "Placeholder",
|
|
1455
|
+
id
|
|
1456
|
+
};
|
|
1503
1457
|
const exprAST = nameToExprAST.get(name);
|
|
1504
1458
|
if (exprAST) return exprAST;
|
|
1505
1459
|
return name;
|
|
@@ -1550,6 +1504,20 @@ function createLambdaParam(index) {
|
|
|
1550
1504
|
*/
|
|
1551
1505
|
function lambda(builder) {
|
|
1552
1506
|
const paramCount = builder.length;
|
|
1507
|
+
const { params, paramSymbols } = createLambdaParams(paramCount);
|
|
1508
|
+
const { bodyAst, bodyDeps } = extractBodyAstAndDeps(builder(...params));
|
|
1509
|
+
const lambdaProxy = createProxyExpressionWithAST(createArrowFunctionAst(bodyAst, paramCount, paramSymbols), filterClosureDeps(bodyDeps, paramSymbols));
|
|
1510
|
+
const existingMeta = getProxyMetadata(lambdaProxy);
|
|
1511
|
+
if (existingMeta) setProxyMetadata(lambdaProxy, {
|
|
1512
|
+
...existingMeta,
|
|
1513
|
+
type: "expression"
|
|
1514
|
+
});
|
|
1515
|
+
return lambdaProxy;
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* 创建 lambda 参数和符号映射
|
|
1519
|
+
*/
|
|
1520
|
+
function createLambdaParams(paramCount) {
|
|
1553
1521
|
const params = [];
|
|
1554
1522
|
const paramSymbols = [];
|
|
1555
1523
|
for (let i = 0; i < paramCount; i++) {
|
|
@@ -1558,43 +1526,50 @@ function lambda(builder) {
|
|
|
1558
1526
|
const meta = getProxyMetadata(param);
|
|
1559
1527
|
if (meta?.rootVariable) paramSymbols.push(meta.rootVariable);
|
|
1560
1528
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1529
|
+
return {
|
|
1530
|
+
params,
|
|
1531
|
+
paramSymbols
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* 从表达式中提取 AST 和依赖
|
|
1536
|
+
*/
|
|
1537
|
+
function extractBodyAstAndDeps(bodyExpr) {
|
|
1564
1538
|
const meta = (typeof bodyExpr === "object" || typeof bodyExpr === "function") && bodyExpr !== null ? getProxyMetadata(bodyExpr) : void 0;
|
|
1565
|
-
if (meta?.ast) {
|
|
1566
|
-
bodyAst
|
|
1567
|
-
bodyDeps
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
bodyDeps = /* @__PURE__ */ new Set();
|
|
1539
|
+
if (meta?.ast) return {
|
|
1540
|
+
bodyAst: meta.ast,
|
|
1541
|
+
bodyDeps: meta.dependencies ?? /* @__PURE__ */ new Set()
|
|
1542
|
+
};
|
|
1543
|
+
else {
|
|
1544
|
+
const bodyDeps = /* @__PURE__ */ new Set();
|
|
1571
1545
|
collectDepsFromArgs([bodyExpr], bodyDeps);
|
|
1546
|
+
return {
|
|
1547
|
+
bodyAst: serializeArgumentToAST(bodyExpr),
|
|
1548
|
+
bodyDeps
|
|
1549
|
+
};
|
|
1572
1550
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
});
|
|
1581
|
-
const arrowFunctionAst = {
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* 创建箭头函数 AST
|
|
1554
|
+
* 使用 Placeholder 节点作为参数,在代码生成时再分配实际参数名
|
|
1555
|
+
*/
|
|
1556
|
+
function createArrowFunctionAst(bodyAst, paramCount, paramSymbols) {
|
|
1557
|
+
return {
|
|
1582
1558
|
type: "ArrowFunctionExpr",
|
|
1583
|
-
params:
|
|
1584
|
-
type: "
|
|
1585
|
-
|
|
1559
|
+
params: Array.from({ length: paramCount }, (_, i) => ({
|
|
1560
|
+
type: "Placeholder",
|
|
1561
|
+
id: paramSymbols[i]
|
|
1586
1562
|
})),
|
|
1587
|
-
body:
|
|
1563
|
+
body: bodyAst
|
|
1588
1564
|
};
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* 过滤掉 lambda 参数依赖,只保留外部闭包变量
|
|
1568
|
+
*/
|
|
1569
|
+
function filterClosureDeps(bodyDeps, paramSymbols) {
|
|
1589
1570
|
const closureDeps = /* @__PURE__ */ new Set();
|
|
1590
1571
|
for (const dep of bodyDeps) if (!paramSymbols.includes(dep)) closureDeps.add(dep);
|
|
1591
|
-
|
|
1592
|
-
const existingMeta = getProxyMetadata(lambdaProxy);
|
|
1593
|
-
if (existingMeta) setProxyMetadata(lambdaProxy, {
|
|
1594
|
-
...existingMeta,
|
|
1595
|
-
type: "expression"
|
|
1596
|
-
});
|
|
1597
|
-
return lambdaProxy;
|
|
1572
|
+
return closureDeps;
|
|
1598
1573
|
}
|
|
1599
1574
|
|
|
1600
1575
|
//#endregion
|