@codehz/json-expr 0.6.0 → 0.6.2
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.d.mts +184 -177
- package/dist/index.mjs +1362 -1143
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/ast-types.ts
|
|
1
|
+
//#region src/types/ast-types.ts
|
|
2
2
|
const PRECEDENCE = {
|
|
3
3
|
"||": 1,
|
|
4
4
|
"??": 1,
|
|
@@ -50,7 +50,7 @@ const BUILTIN_CONSTRUCTORS = new Set([
|
|
|
50
50
|
]);
|
|
51
51
|
|
|
52
52
|
//#endregion
|
|
53
|
-
//#region src/generate.ts
|
|
53
|
+
//#region src/core/generate.ts
|
|
54
54
|
/**
|
|
55
55
|
* 创建新的生成上下文
|
|
56
56
|
*/
|
|
@@ -314,749 +314,463 @@ function transformPlaceholders(node, transform) {
|
|
|
314
314
|
default: return node;
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
|
-
|
|
318
|
-
//#endregion
|
|
319
|
-
//#region src/proxy-metadata.ts
|
|
320
317
|
/**
|
|
321
|
-
*
|
|
318
|
+
* 统计 AST 中各标识符名称的出现次数
|
|
319
|
+
* 用于判断子表达式是否应该内联(引用次数 = 1 则内联,> 1 则推迟为独立编译)
|
|
322
320
|
*/
|
|
323
|
-
|
|
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
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/core/parser/constants.ts
|
|
324
369
|
/**
|
|
325
|
-
*
|
|
370
|
+
* 解析器常量定义
|
|
326
371
|
*/
|
|
327
|
-
function setProxyMetadata(proxy, metadata) {
|
|
328
|
-
proxyMetadata.set(proxy, metadata);
|
|
329
|
-
}
|
|
330
372
|
/**
|
|
331
|
-
*
|
|
373
|
+
* 运算符列表,按长度排序(长的在前)以确保正确匹配
|
|
332
374
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
"
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
345
|
-
"
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
"
|
|
350
|
-
"
|
|
375
|
+
const OPERATORS = [
|
|
376
|
+
"instanceof",
|
|
377
|
+
">>>",
|
|
378
|
+
"===",
|
|
379
|
+
"!==",
|
|
380
|
+
"&&",
|
|
381
|
+
"||",
|
|
382
|
+
"??",
|
|
383
|
+
"==",
|
|
384
|
+
"!=",
|
|
385
|
+
"<=",
|
|
386
|
+
">=",
|
|
387
|
+
"<<",
|
|
388
|
+
">>",
|
|
389
|
+
"**",
|
|
390
|
+
"in",
|
|
391
|
+
"+",
|
|
392
|
+
"-",
|
|
393
|
+
"*",
|
|
394
|
+
"/",
|
|
395
|
+
"%",
|
|
396
|
+
"<",
|
|
397
|
+
">",
|
|
398
|
+
"&",
|
|
399
|
+
"|",
|
|
400
|
+
"^"
|
|
351
401
|
];
|
|
352
402
|
/**
|
|
353
|
-
*
|
|
403
|
+
* 关键字运算符(需要后面不能跟标识符字符)
|
|
354
404
|
*/
|
|
355
|
-
|
|
356
|
-
return {
|
|
357
|
-
type: "Placeholder",
|
|
358
|
-
id
|
|
359
|
-
};
|
|
360
|
-
}
|
|
405
|
+
const KEYWORD_OPERATORS = new Set(["in", "instanceof"]);
|
|
361
406
|
/**
|
|
362
|
-
*
|
|
407
|
+
* 转义字符映射
|
|
363
408
|
*/
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
409
|
+
const ESCAPE_CHARS = {
|
|
410
|
+
n: "\n",
|
|
411
|
+
r: "\r",
|
|
412
|
+
t: " ",
|
|
413
|
+
"\\": "\\",
|
|
414
|
+
"'": "'",
|
|
415
|
+
"\"": "\"",
|
|
416
|
+
"`": "`"
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/core/parser/utils.ts
|
|
370
421
|
/**
|
|
371
|
-
*
|
|
422
|
+
* 解析器工具函数
|
|
372
423
|
*/
|
|
373
|
-
function numberLiteral(value) {
|
|
374
|
-
return {
|
|
375
|
-
type: "NumberLiteral",
|
|
376
|
-
value,
|
|
377
|
-
raw: String(value)
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
424
|
/**
|
|
381
|
-
*
|
|
425
|
+
* 判断字符是否为数字 0-9
|
|
382
426
|
*/
|
|
383
|
-
function
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
value,
|
|
387
|
-
quote
|
|
388
|
-
};
|
|
427
|
+
function isDigit(ch) {
|
|
428
|
+
const code = ch.charCodeAt(0);
|
|
429
|
+
return code >= 48 && code <= 57;
|
|
389
430
|
}
|
|
390
431
|
/**
|
|
391
|
-
*
|
|
432
|
+
* 判断字符是否为十六进制数字 0-9, A-F, a-f
|
|
392
433
|
*/
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
object,
|
|
397
|
-
property,
|
|
398
|
-
computed: false,
|
|
399
|
-
optional: false
|
|
400
|
-
};
|
|
434
|
+
function isHexDigit(ch) {
|
|
435
|
+
const code = ch.charCodeAt(0);
|
|
436
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
|
401
437
|
}
|
|
402
438
|
/**
|
|
403
|
-
*
|
|
439
|
+
* 判断字符是否可以作为标识符的开头(字母、下划线、$)
|
|
404
440
|
*/
|
|
405
|
-
function
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
callee,
|
|
409
|
-
arguments: arguments_,
|
|
410
|
-
optional: false
|
|
411
|
-
};
|
|
441
|
+
function isIdentifierStart(ch) {
|
|
442
|
+
const code = ch.charCodeAt(0);
|
|
443
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95 || code === 36;
|
|
412
444
|
}
|
|
413
445
|
/**
|
|
414
|
-
*
|
|
446
|
+
* 判断字符是否可以作为标识符的一部分(字母、数字、下划线、$)
|
|
415
447
|
*/
|
|
416
|
-
function
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
elements
|
|
420
|
-
};
|
|
448
|
+
function isIdentifierPart(ch) {
|
|
449
|
+
const code = ch.charCodeAt(0);
|
|
450
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 95 || code === 36;
|
|
421
451
|
}
|
|
452
|
+
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region src/core/parser/tokenizer.ts
|
|
422
455
|
/**
|
|
423
|
-
*
|
|
456
|
+
* 词法分析器 - 负责源代码的逐字符扫描和标记识别
|
|
424
457
|
*/
|
|
425
|
-
function getTypedArrayConstructor(value) {
|
|
426
|
-
for (const constructorName of typedArrayConstructors) {
|
|
427
|
-
const Constructor = globalThis[constructorName];
|
|
428
|
-
if (Constructor && value instanceof Constructor) return Constructor;
|
|
429
|
-
}
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
458
|
/**
|
|
433
|
-
*
|
|
434
|
-
* - Proxy Variable/Expression:使用 ast 或占位符标识符
|
|
435
|
-
* - 数组:返回 ArrayExpr 节点
|
|
436
|
-
* - 对象:返回 ObjectExpr 节点
|
|
437
|
-
* - 原始值:返回对应的字面量节点
|
|
438
|
-
* - Date, RegExp, BigInt, URL, URLSearchParams, Map, Set, TypedArray, DataView: 构造函数调用
|
|
459
|
+
* Tokenizer 类 - 提供词法分析的基础功能
|
|
439
460
|
*/
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (meta.rootVariable) return placeholder(meta.rootVariable);
|
|
446
|
-
}
|
|
461
|
+
var Tokenizer = class {
|
|
462
|
+
pos = 0;
|
|
463
|
+
source;
|
|
464
|
+
constructor(source) {
|
|
465
|
+
this.source = source;
|
|
447
466
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
467
|
+
/**
|
|
468
|
+
* 查看当前位置的字符,不移动位置
|
|
469
|
+
*/
|
|
470
|
+
peek() {
|
|
471
|
+
return this.source[this.pos] || "";
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 查看指定偏移量位置的字符,不移动位置
|
|
475
|
+
*/
|
|
476
|
+
peekAt(offset) {
|
|
477
|
+
return this.source[this.pos + offset] || "";
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* 前进一个字符并返回该字符
|
|
481
|
+
*/
|
|
482
|
+
advance() {
|
|
483
|
+
return this.source[this.pos++] || "";
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 期望当前字符是指定字符,否则抛出错误
|
|
487
|
+
*/
|
|
488
|
+
expect(ch) {
|
|
489
|
+
if (this.peek() !== ch) throw new Error(`Expected '${ch}' at position ${this.pos}, got '${this.peek()}'`);
|
|
490
|
+
this.advance();
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* 跳过空白字符
|
|
494
|
+
*/
|
|
495
|
+
skipWhitespace() {
|
|
496
|
+
while (/\s/.test(this.peek())) this.advance();
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* 尝试匹配并消费运算符
|
|
500
|
+
* 返回匹配到的运算符,如果没有匹配则返回 null
|
|
501
|
+
*/
|
|
502
|
+
peekOperator() {
|
|
503
|
+
for (const op of OPERATORS) {
|
|
504
|
+
if (!this.source.startsWith(op, this.pos)) continue;
|
|
505
|
+
if (KEYWORD_OPERATORS.has(op)) {
|
|
506
|
+
const nextChar = this.source[this.pos + op.length];
|
|
507
|
+
if (nextChar && isIdentifierPart(nextChar)) continue;
|
|
508
|
+
}
|
|
509
|
+
return op;
|
|
455
510
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 尝试匹配并消费关键字
|
|
515
|
+
* 如果匹配成功则移动位置并返回 true,否则返回 false
|
|
516
|
+
*/
|
|
517
|
+
matchKeyword(keyword) {
|
|
518
|
+
if (this.source.startsWith(keyword, this.pos)) {
|
|
519
|
+
const nextChar = this.source[this.pos + keyword.length];
|
|
520
|
+
if (!nextChar || !isIdentifierPart(nextChar)) {
|
|
521
|
+
this.pos += keyword.length;
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
463
524
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/core/parser/primary-parser.ts
|
|
531
|
+
/**
|
|
532
|
+
* PrimaryParser 类 - 继承 Tokenizer,提供基本类型的解析功能
|
|
533
|
+
*/
|
|
534
|
+
var PrimaryParser = class extends Tokenizer {
|
|
535
|
+
/**
|
|
536
|
+
* 解析数字字面量
|
|
537
|
+
*/
|
|
538
|
+
parseNumber() {
|
|
539
|
+
const start = this.pos;
|
|
540
|
+
if (this.peek() === "0") {
|
|
541
|
+
const next = this.peekAt(1)?.toLowerCase();
|
|
542
|
+
if (next === "x" || next === "o" || next === "b") {
|
|
543
|
+
this.advance();
|
|
544
|
+
this.advance();
|
|
545
|
+
while (isHexDigit(this.peek())) this.advance();
|
|
546
|
+
const raw = this.source.slice(start, this.pos);
|
|
547
|
+
return {
|
|
548
|
+
type: "NumberLiteral",
|
|
549
|
+
value: Number(raw),
|
|
550
|
+
raw
|
|
551
|
+
};
|
|
552
|
+
}
|
|
470
553
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
554
|
+
while (isDigit(this.peek())) this.advance();
|
|
555
|
+
if (this.peek() === "." && isDigit(this.peekAt(1))) {
|
|
556
|
+
this.advance();
|
|
557
|
+
while (isDigit(this.peek())) this.advance();
|
|
475
558
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return callExpr(identifier(constructorName), [arrayExpr(values)]);
|
|
559
|
+
if (this.peek()?.toLowerCase() === "e") {
|
|
560
|
+
this.advance();
|
|
561
|
+
if (this.peek() === "+" || this.peek() === "-") this.advance();
|
|
562
|
+
while (isDigit(this.peek())) this.advance();
|
|
481
563
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
564
|
+
const raw = this.source.slice(start, this.pos);
|
|
565
|
+
return {
|
|
566
|
+
type: "NumberLiteral",
|
|
567
|
+
value: Number(raw),
|
|
568
|
+
raw
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* 解析字符串字面量
|
|
573
|
+
*/
|
|
574
|
+
parseString() {
|
|
575
|
+
const quote = this.peek();
|
|
576
|
+
this.advance();
|
|
577
|
+
let value = "";
|
|
578
|
+
while (this.pos < this.source.length && this.peek() !== quote) if (this.peek() === "\\") {
|
|
579
|
+
this.advance();
|
|
580
|
+
const escaped = this.peek();
|
|
581
|
+
value += ESCAPE_CHARS[escaped] ?? escaped;
|
|
582
|
+
this.advance();
|
|
583
|
+
} else {
|
|
584
|
+
value += this.peek();
|
|
585
|
+
this.advance();
|
|
486
586
|
}
|
|
487
|
-
|
|
587
|
+
this.expect(quote);
|
|
488
588
|
return {
|
|
489
|
-
type: "
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
key: /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? identifier(k) : stringLiteral(k),
|
|
493
|
-
value: serializeArgumentToAST(v),
|
|
494
|
-
computed: false,
|
|
495
|
-
shorthand: false
|
|
496
|
-
};
|
|
497
|
-
})
|
|
589
|
+
type: "StringLiteral",
|
|
590
|
+
value,
|
|
591
|
+
quote
|
|
498
592
|
};
|
|
499
593
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
*/
|
|
515
|
-
function collectDepsFromArgs(args, deps) {
|
|
516
|
-
for (const arg of args) if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
517
|
-
const meta = getProxyMetadata(arg);
|
|
518
|
-
if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
|
|
519
|
-
else if (Array.isArray(arg)) collectDepsFromArgs(arg, deps);
|
|
520
|
-
else if (typeof arg === "object") collectDepsFromArgs(Object.values(arg), deps);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* 创建 Proxy 的公共 handler
|
|
525
|
-
*/
|
|
526
|
-
function createProxyHandler(ast, deps) {
|
|
527
|
-
return {
|
|
528
|
-
get(_target, prop) {
|
|
529
|
-
if (typeof prop === "symbol") return void 0;
|
|
530
|
-
return createProxyExpressionWithAST(memberExpr(ast, identifier(String(prop))), deps);
|
|
531
|
-
},
|
|
532
|
-
apply(_target, _thisArg, args) {
|
|
533
|
-
const callAst = callExpr(ast, args.map(serializeArgumentToAST));
|
|
534
|
-
const newDeps = new Set(deps);
|
|
535
|
-
collectDepsFromArgs(args, newDeps);
|
|
536
|
-
return createProxyExpressionWithAST(callAst, newDeps);
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* 创建根 Variable Proxy
|
|
542
|
-
* 拦截属性访问,返回新的 expression proxy
|
|
543
|
-
* 不可直接调用(apply 应该只在链式调用后可用)
|
|
544
|
-
*
|
|
545
|
-
* @param id - 变量的唯一标识 Symbol
|
|
546
|
-
* @returns Proxy 包装的 Variable
|
|
547
|
-
*/
|
|
548
|
-
function createProxyVariable(id) {
|
|
549
|
-
const deps = new Set([id]);
|
|
550
|
-
const ast = placeholder(id);
|
|
551
|
-
const proxy = new Proxy(function() {}, createProxyHandler(ast, deps));
|
|
552
|
-
setProxyMetadata(proxy, {
|
|
553
|
-
type: "variable",
|
|
554
|
-
rootVariable: id,
|
|
555
|
-
dependencies: deps
|
|
556
|
-
});
|
|
557
|
-
return proxy;
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* 创建带完整 AST 的 Proxy(方法调用后)
|
|
561
|
-
* 可以继续链式访问和调用
|
|
562
|
-
*
|
|
563
|
-
* @param ast - 完整的表达式 AST
|
|
564
|
-
* @param deps - 依赖集合
|
|
565
|
-
* @returns Proxy 包装的 Expression
|
|
566
|
-
*/
|
|
567
|
-
function createProxyExpressionWithAST(ast, deps) {
|
|
568
|
-
const proxy = new Proxy(function() {}, createProxyHandler(ast, deps));
|
|
569
|
-
setProxyMetadata(proxy, {
|
|
570
|
-
type: "expression",
|
|
571
|
-
ast,
|
|
572
|
-
dependencies: deps
|
|
573
|
-
});
|
|
574
|
-
return proxy;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
//#endregion
|
|
578
|
-
//#region src/variable.ts
|
|
579
|
-
/**
|
|
580
|
-
* 跟踪每个 variable 的唯一 Symbol ID
|
|
581
|
-
*/
|
|
582
|
-
const variableIds = /* @__PURE__ */ new WeakMap();
|
|
583
|
-
/**
|
|
584
|
-
* 计数器用于生成唯一变量 ID
|
|
585
|
-
*/
|
|
586
|
-
let variableCounter = 0;
|
|
587
|
-
/**
|
|
588
|
-
* 创建一个类型化变量
|
|
589
|
-
* 返回 Proxy 对象,支持链式属性访问和方法调用
|
|
590
|
-
*
|
|
591
|
-
* @example
|
|
592
|
-
* ```ts
|
|
593
|
-
* const x = variable<number>();
|
|
594
|
-
* const config = variable<{ timeout: number }>();
|
|
595
|
-
* const timeout = config.timeout; // Proxy expression
|
|
596
|
-
* ```
|
|
597
|
-
*/
|
|
598
|
-
function variable() {
|
|
599
|
-
const id = Symbol(`var_${variableCounter++}`);
|
|
600
|
-
const proxy = createProxyVariable(id);
|
|
601
|
-
variableIds.set(proxy, id);
|
|
602
|
-
return proxy;
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* 获取 variable 的唯一 Symbol ID
|
|
606
|
-
*/
|
|
607
|
-
function getVariableId(variable) {
|
|
608
|
-
if (typeof variable !== "object" && typeof variable !== "function" || variable === null) return void 0;
|
|
609
|
-
return variableIds.get(variable);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
//#endregion
|
|
613
|
-
//#region src/compile.ts
|
|
614
|
-
const ALLOWED_GLOBALS = new Set([
|
|
615
|
-
"Math",
|
|
616
|
-
"JSON",
|
|
617
|
-
"Date",
|
|
618
|
-
"RegExp",
|
|
619
|
-
"Number",
|
|
620
|
-
"String",
|
|
621
|
-
"Boolean",
|
|
622
|
-
"Array",
|
|
623
|
-
"Object",
|
|
624
|
-
"undefined",
|
|
625
|
-
"NaN",
|
|
626
|
-
"Infinity",
|
|
627
|
-
"isNaN",
|
|
628
|
-
"isFinite",
|
|
629
|
-
"parseInt",
|
|
630
|
-
"parseFloat",
|
|
631
|
-
"BigInt",
|
|
632
|
-
"URL",
|
|
633
|
-
"URLSearchParams",
|
|
634
|
-
"Map",
|
|
635
|
-
"Set",
|
|
636
|
-
"Int8Array",
|
|
637
|
-
"Uint8Array",
|
|
638
|
-
"Uint8ClampedArray",
|
|
639
|
-
"Int16Array",
|
|
640
|
-
"Uint16Array",
|
|
641
|
-
"Int32Array",
|
|
642
|
-
"Uint32Array",
|
|
643
|
-
"Float32Array",
|
|
644
|
-
"Float64Array",
|
|
645
|
-
"BigInt64Array",
|
|
646
|
-
"BigUint64Array",
|
|
647
|
-
"ArrayBuffer",
|
|
648
|
-
"DataView"
|
|
649
|
-
]);
|
|
650
|
-
/**
|
|
651
|
-
* 将 Proxy Expression 编译为可序列化的 JSON 结构
|
|
652
|
-
*
|
|
653
|
-
* @template TResult - 表达式结果类型
|
|
654
|
-
* @param expression - Proxy Expression,或包含 Proxy 的对象/数组/原始值
|
|
655
|
-
* @param variables - 所有使用的变量定义
|
|
656
|
-
* @param options - 编译选项
|
|
657
|
-
* @returns 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
658
|
-
*
|
|
659
|
-
* @throws 如果传入无效的表达式或未定义的变量引用
|
|
660
|
-
*
|
|
661
|
-
* @example
|
|
662
|
-
* ```ts
|
|
663
|
-
* const x = variable<number>()
|
|
664
|
-
* const y = variable<number>()
|
|
665
|
-
* const sum = expr({ x, y })("x + y")
|
|
666
|
-
* const result = expr({ sum, x })("sum * x")
|
|
667
|
-
* const compiled = compile(result, { x, y })
|
|
668
|
-
* // => [["x", "y"], "($[0]+$[1])*$[0]"]
|
|
669
|
-
* ```
|
|
670
|
-
*/
|
|
671
|
-
function compile(expression, variables, _options = {}) {
|
|
672
|
-
const ast = serializeArgumentToAST(expression);
|
|
673
|
-
const variableOrder = [];
|
|
674
|
-
const variableToIndex = /* @__PURE__ */ new Map();
|
|
675
|
-
const symbolToName = /* @__PURE__ */ new Map();
|
|
676
|
-
for (const [name, value] of Object.entries(variables)) {
|
|
677
|
-
if (!variableToIndex.has(name)) {
|
|
678
|
-
variableToIndex.set(name, variableOrder.length);
|
|
679
|
-
variableOrder.push(name);
|
|
680
|
-
}
|
|
681
|
-
const id = getVariableId(value);
|
|
682
|
-
if (id) symbolToName.set(id, name);
|
|
683
|
-
}
|
|
684
|
-
const placeholderTransformed = transformPlaceholders(ast, (id) => {
|
|
685
|
-
const name = symbolToName.get(id);
|
|
686
|
-
if (!name) return null;
|
|
687
|
-
const index = variableToIndex.get(name);
|
|
688
|
-
if (index === void 0) return null;
|
|
689
|
-
return `$[${index}]`;
|
|
690
|
-
});
|
|
691
|
-
const undefinedVars = [];
|
|
692
|
-
const transformed = transformIdentifiers(placeholderTransformed, (name) => {
|
|
693
|
-
if (name.startsWith("$[") && /^\$\[\d+\]$/.test(name)) return name;
|
|
694
|
-
if (/^_\d+$/.test(name)) return name;
|
|
695
|
-
const index = variableToIndex.get(name);
|
|
696
|
-
if (index !== void 0) return `$[${index}]`;
|
|
697
|
-
if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
|
|
698
|
-
return name;
|
|
699
|
-
});
|
|
700
|
-
if (undefinedVars.length > 0) {
|
|
701
|
-
const uniqueVars = [...new Set(undefinedVars)];
|
|
702
|
-
throw new Error(`Undefined variable(s): ${uniqueVars.join(", ")}`);
|
|
703
|
-
}
|
|
704
|
-
const topLevelExprs = [];
|
|
705
|
-
compileAst(transformed, {
|
|
706
|
-
nextParamIndex: 0,
|
|
707
|
-
expressionStack: [topLevelExprs],
|
|
708
|
-
nextIndex: variableOrder.length,
|
|
709
|
-
variableCount: variableOrder.length
|
|
710
|
-
});
|
|
711
|
-
return [variableOrder, ...topLevelExprs];
|
|
712
|
-
}
|
|
713
|
-
function currentExprs(ctx) {
|
|
714
|
-
return ctx.expressionStack[ctx.expressionStack.length - 1];
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* 提取 AST 中所有 ArrowFunctionExpr 节点,编译为 FnNode,
|
|
718
|
-
* 并将原始位置替换为 $[N] 标识符引用。
|
|
719
|
-
*/
|
|
720
|
-
function extractAndCompileArrowFunctions(node, ctx) {
|
|
721
|
-
switch (node.type) {
|
|
722
|
-
case "ArrowFunctionExpr": return {
|
|
723
|
-
type: "Identifier",
|
|
724
|
-
name: `$[${compileArrowFunction(node, ctx)}]`
|
|
725
|
-
};
|
|
726
|
-
case "BinaryExpr": return {
|
|
727
|
-
...node,
|
|
728
|
-
left: extractAndCompileArrowFunctions(node.left, ctx),
|
|
729
|
-
right: extractAndCompileArrowFunctions(node.right, ctx)
|
|
730
|
-
};
|
|
731
|
-
case "UnaryExpr": return {
|
|
732
|
-
...node,
|
|
733
|
-
argument: extractAndCompileArrowFunctions(node.argument, ctx)
|
|
734
|
-
};
|
|
735
|
-
case "ConditionalExpr": return {
|
|
736
|
-
...node,
|
|
737
|
-
test: extractAndCompileArrowFunctions(node.test, ctx),
|
|
738
|
-
consequent: extractAndCompileArrowFunctions(node.consequent, ctx),
|
|
739
|
-
alternate: extractAndCompileArrowFunctions(node.alternate, ctx)
|
|
740
|
-
};
|
|
741
|
-
case "MemberExpr": return {
|
|
742
|
-
...node,
|
|
743
|
-
object: extractAndCompileArrowFunctions(node.object, ctx),
|
|
744
|
-
property: node.computed ? extractAndCompileArrowFunctions(node.property, ctx) : node.property
|
|
745
|
-
};
|
|
746
|
-
case "CallExpr": return {
|
|
747
|
-
...node,
|
|
748
|
-
callee: extractAndCompileArrowFunctions(node.callee, ctx),
|
|
749
|
-
arguments: node.arguments.map((arg) => extractAndCompileArrowFunctions(arg, ctx))
|
|
750
|
-
};
|
|
751
|
-
case "ArrayExpr": return {
|
|
752
|
-
...node,
|
|
753
|
-
elements: node.elements.map((el) => extractAndCompileArrowFunctions(el, ctx))
|
|
754
|
-
};
|
|
755
|
-
case "ObjectExpr": return {
|
|
756
|
-
...node,
|
|
757
|
-
properties: node.properties.map((prop) => ({
|
|
758
|
-
...prop,
|
|
759
|
-
key: prop.computed ? extractAndCompileArrowFunctions(prop.key, ctx) : prop.key,
|
|
760
|
-
value: extractAndCompileArrowFunctions(prop.value, ctx)
|
|
761
|
-
}))
|
|
762
|
-
};
|
|
763
|
-
default: return node;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
function compileAst(node, ctx) {
|
|
767
|
-
if (node.type === "BinaryExpr" && (node.operator === "||" || node.operator === "&&" || node.operator === "??")) return compileShortCircuit(node, ctx);
|
|
768
|
-
if (node.type === "ConditionalExpr") return compileConditional(node, ctx);
|
|
769
|
-
if (node.type === "ArrowFunctionExpr") return compileArrowFunction(node, ctx);
|
|
770
|
-
const exprStr = generate(extractAndCompileArrowFunctions(node, ctx));
|
|
771
|
-
currentExprs(ctx).push(exprStr);
|
|
772
|
-
return ctx.nextIndex++;
|
|
773
|
-
}
|
|
774
|
-
function compileShortCircuit(node, ctx) {
|
|
775
|
-
const exprs = currentExprs(ctx);
|
|
776
|
-
const leftIdx = compileAst(node.left, ctx);
|
|
777
|
-
const branchConditions = {
|
|
778
|
-
"||": `$[${leftIdx}]`,
|
|
779
|
-
"&&": `!$[${leftIdx}]`,
|
|
780
|
-
"??": `$[${leftIdx}]!=null`
|
|
781
|
-
};
|
|
782
|
-
const branchIdx = exprs.length;
|
|
783
|
-
exprs.push([
|
|
784
|
-
"br",
|
|
785
|
-
branchConditions[node.operator],
|
|
786
|
-
0
|
|
787
|
-
]);
|
|
788
|
-
ctx.nextIndex++;
|
|
789
|
-
compileAst(node.right, ctx);
|
|
790
|
-
const skipCount = exprs.length - branchIdx - 1;
|
|
791
|
-
exprs[branchIdx][2] = skipCount;
|
|
792
|
-
const phiIdx = ctx.nextIndex++;
|
|
793
|
-
exprs.push(["phi"]);
|
|
794
|
-
return phiIdx;
|
|
795
|
-
}
|
|
796
|
-
function compileConditional(node, ctx) {
|
|
797
|
-
const exprs = currentExprs(ctx);
|
|
798
|
-
const testIdx = compileAst(node.test, ctx);
|
|
799
|
-
const branchIdx = exprs.length;
|
|
800
|
-
exprs.push([
|
|
801
|
-
"br",
|
|
802
|
-
`$[${testIdx}]`,
|
|
803
|
-
0
|
|
804
|
-
]);
|
|
805
|
-
ctx.nextIndex++;
|
|
806
|
-
compileAst(node.alternate, ctx);
|
|
807
|
-
const jmpIdx = exprs.length;
|
|
808
|
-
exprs.push(["jmp", 0]);
|
|
809
|
-
ctx.nextIndex++;
|
|
810
|
-
compileAst(node.consequent, ctx);
|
|
811
|
-
const thenEndIdx = exprs.length;
|
|
812
|
-
exprs[branchIdx][2] = jmpIdx - branchIdx;
|
|
813
|
-
exprs[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
|
|
814
|
-
const phiIdx = ctx.nextIndex++;
|
|
815
|
-
exprs.push(["phi"]);
|
|
816
|
-
return phiIdx;
|
|
817
|
-
}
|
|
818
|
-
function compileArrowFunction(node, ctx) {
|
|
819
|
-
const paramCount = node.params.length;
|
|
820
|
-
const fnIndex = ctx.nextIndex++;
|
|
821
|
-
const paramMapping = /* @__PURE__ */ new Map();
|
|
822
|
-
for (const param of node.params) if (param.type === "Placeholder") {
|
|
823
|
-
const paramName = `_${ctx.nextParamIndex++}`;
|
|
824
|
-
paramMapping.set(param.id, paramName);
|
|
825
|
-
}
|
|
826
|
-
const transformedBody = transformPlaceholders(node.body, (id) => {
|
|
827
|
-
return paramMapping.get(id) ?? null;
|
|
828
|
-
});
|
|
829
|
-
const lambdaStmts = [];
|
|
830
|
-
ctx.expressionStack.push(lambdaStmts);
|
|
831
|
-
compileAst(transformedBody, ctx);
|
|
832
|
-
ctx.expressionStack.pop();
|
|
833
|
-
const fnNode = [
|
|
834
|
-
"fn",
|
|
835
|
-
paramCount,
|
|
836
|
-
...lambdaStmts
|
|
837
|
-
];
|
|
838
|
-
currentExprs(ctx).push(fnNode);
|
|
839
|
-
return fnIndex;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
//#endregion
|
|
843
|
-
//#region src/evaluate.ts
|
|
844
|
-
/**
|
|
845
|
-
* 缓存已构造的求值函数,以提升重复执行性能
|
|
846
|
-
*/
|
|
847
|
-
const evaluatorCache = /* @__PURE__ */ new Map();
|
|
848
|
-
/**
|
|
849
|
-
* 判断是否是 FnNode
|
|
850
|
-
*/
|
|
851
|
-
function isFnNode(expr) {
|
|
852
|
-
return Array.isArray(expr) && expr[0] === "fn" && typeof expr[1] === "number";
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* 判断是否是控制流节点
|
|
856
|
-
*/
|
|
857
|
-
function isControlFlowNode(expr) {
|
|
858
|
-
return Array.isArray(expr) && (expr[0] === "br" || expr[0] === "jmp" || expr[0] === "phi");
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* 生成 lambda 函数代码
|
|
862
|
-
*
|
|
863
|
-
* @param fnNode - FnNode 节点
|
|
864
|
-
* @param indexCounter - 全局索引计数器
|
|
865
|
-
* @param paramCounter - 参数计数器
|
|
866
|
-
* @returns lambda 函数代码字符串
|
|
867
|
-
*/
|
|
868
|
-
function generateLambdaCode(fnNode, indexCounter, paramCounter) {
|
|
869
|
-
const [, paramCount, ...stmts] = fnNode;
|
|
870
|
-
const paramStartIndex = paramCounter.value;
|
|
871
|
-
paramCounter.value += paramCount;
|
|
872
|
-
return `(${Array.from({ length: paramCount }, (_, i) => `_${paramStartIndex + i}`).join(",")})=>{${translateStmts(stmts, indexCounter, paramCounter)}}`;
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* 翻译表达式列表为 JavaScript 代码
|
|
876
|
-
* 使用全局索引计数器分配 $[N] 槽位
|
|
877
|
-
*/
|
|
878
|
-
function translateStmts(expressions, indexCounter, paramCounter) {
|
|
879
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
880
|
-
const expr = expressions[i];
|
|
881
|
-
if (!expr) continue;
|
|
882
|
-
if (isControlFlowNode(expr)) {
|
|
883
|
-
const [type, , offset] = expr;
|
|
884
|
-
if ((type === "br" || type === "jmp") && offset !== void 0) {
|
|
885
|
-
const target = i + offset + 1;
|
|
886
|
-
if (target < 0 || target > expressions.length) throw new Error(`Unable to translate: invalid jump target at index ${i}. Target would be ${target}, but expression length is ${expressions.length}`);
|
|
887
|
-
}
|
|
594
|
+
/**
|
|
595
|
+
* 解析数组字面量
|
|
596
|
+
*/
|
|
597
|
+
parseArray() {
|
|
598
|
+
this.expect("[");
|
|
599
|
+
const elements = [];
|
|
600
|
+
this.skipWhitespace();
|
|
601
|
+
while (this.peek() !== "]") {
|
|
602
|
+
elements.push(this.parseExpression());
|
|
603
|
+
this.skipWhitespace();
|
|
604
|
+
if (this.peek() === ",") {
|
|
605
|
+
this.advance();
|
|
606
|
+
this.skipWhitespace();
|
|
607
|
+
} else break;
|
|
888
608
|
}
|
|
609
|
+
this.expect("]");
|
|
610
|
+
return {
|
|
611
|
+
type: "ArrayExpr",
|
|
612
|
+
elements
|
|
613
|
+
};
|
|
889
614
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
615
|
+
/**
|
|
616
|
+
* 解析对象字面量
|
|
617
|
+
*/
|
|
618
|
+
parseObject() {
|
|
619
|
+
this.expect("{");
|
|
620
|
+
const properties = [];
|
|
621
|
+
this.skipWhitespace();
|
|
622
|
+
while (this.peek() !== "}") {
|
|
623
|
+
const prop = this.parseObjectProperty();
|
|
624
|
+
properties.push(prop);
|
|
625
|
+
this.skipWhitespace();
|
|
626
|
+
if (this.peek() === ",") {
|
|
627
|
+
this.advance();
|
|
628
|
+
this.skipWhitespace();
|
|
629
|
+
} else break;
|
|
630
|
+
}
|
|
631
|
+
this.expect("}");
|
|
632
|
+
return {
|
|
633
|
+
type: "ObjectExpr",
|
|
634
|
+
properties
|
|
635
|
+
};
|
|
900
636
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
}
|
|
928
|
-
else {
|
|
929
|
-
const [type] = expr;
|
|
930
|
-
if (type === "br") {
|
|
931
|
-
processed.add(i);
|
|
932
|
-
indexCounter.value++;
|
|
933
|
-
const [, condition, skipCount] = expr;
|
|
934
|
-
const falseBlockEnd = i + skipCount;
|
|
935
|
-
let jmpIdx = -1;
|
|
936
|
-
let trueBlockEnd = falseBlockEnd + 1;
|
|
937
|
-
if (falseBlockEnd < expressions.length) {
|
|
938
|
-
const lastInFalseBranch = expressions[falseBlockEnd];
|
|
939
|
-
if (Array.isArray(lastInFalseBranch) && lastInFalseBranch[0] === "jmp") {
|
|
940
|
-
jmpIdx = falseBlockEnd;
|
|
941
|
-
const jmpOffset = lastInFalseBranch[1];
|
|
942
|
-
trueBlockEnd = jmpIdx + jmpOffset;
|
|
943
|
-
processed.add(jmpIdx);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
const hasElse = jmpIdx >= 0;
|
|
947
|
-
lines.push(`${indent}if (!(${condition})) {`);
|
|
948
|
-
translateRange(i + 1, falseBlockEnd + 1, indent + " ");
|
|
949
|
-
if (hasElse) {
|
|
950
|
-
lines.push(`${indent}} else {`);
|
|
951
|
-
translateRange(jmpIdx + 1, trueBlockEnd + 1, indent + " ");
|
|
952
|
-
lines.push(`${indent}}`);
|
|
953
|
-
i = trueBlockEnd + 1;
|
|
954
|
-
} else {
|
|
955
|
-
lines.push(`${indent}}`);
|
|
956
|
-
i = falseBlockEnd + 1;
|
|
957
|
-
}
|
|
958
|
-
} else if (type === "phi") {
|
|
959
|
-
processed.add(i);
|
|
960
|
-
indexCounter.value++;
|
|
961
|
-
lines.push(`${indent}$[${varIdx}] = $lastValue;`);
|
|
962
|
-
i++;
|
|
963
|
-
} else if (type === "jmp") {
|
|
964
|
-
indexCounter.value++;
|
|
965
|
-
throw new Error(`Unable to translate: unexpected jmp at index ${i}. This should have been paired with a preceding br. Unsupported control flow structure.`);
|
|
966
|
-
} else throw new Error(`Unable to translate: unknown control flow node type.`);
|
|
967
|
-
}
|
|
637
|
+
/**
|
|
638
|
+
* 解析对象属性
|
|
639
|
+
*/
|
|
640
|
+
parseObjectProperty() {
|
|
641
|
+
this.skipWhitespace();
|
|
642
|
+
let key;
|
|
643
|
+
let computed = false;
|
|
644
|
+
if (this.peek() === "[") {
|
|
645
|
+
this.advance();
|
|
646
|
+
this.skipWhitespace();
|
|
647
|
+
key = this.parseExpression();
|
|
648
|
+
this.skipWhitespace();
|
|
649
|
+
this.expect("]");
|
|
650
|
+
computed = true;
|
|
651
|
+
} else if (this.peek() === "\"" || this.peek() === "'") key = this.parseString();
|
|
652
|
+
else key = this.parseIdentifier();
|
|
653
|
+
this.skipWhitespace();
|
|
654
|
+
if (this.peek() === ":") {
|
|
655
|
+
this.advance();
|
|
656
|
+
this.skipWhitespace();
|
|
657
|
+
const value = this.parseExpression();
|
|
658
|
+
return {
|
|
659
|
+
key,
|
|
660
|
+
value,
|
|
661
|
+
computed,
|
|
662
|
+
shorthand: false
|
|
663
|
+
};
|
|
968
664
|
}
|
|
665
|
+
if (key.type !== "Identifier") throw new Error("Shorthand property must be an identifier");
|
|
666
|
+
return {
|
|
667
|
+
key,
|
|
668
|
+
value: key,
|
|
669
|
+
computed: false,
|
|
670
|
+
shorthand: true
|
|
671
|
+
};
|
|
969
672
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
673
|
+
/**
|
|
674
|
+
* 解析标识符
|
|
675
|
+
*/
|
|
676
|
+
parseIdentifier() {
|
|
677
|
+
const start = this.pos;
|
|
678
|
+
while (isIdentifierPart(this.peek())) this.advance();
|
|
679
|
+
const name = this.source.slice(start, this.pos);
|
|
680
|
+
if (!name) throw new Error(`Expected identifier at position ${this.pos}`);
|
|
681
|
+
return {
|
|
682
|
+
type: "Identifier",
|
|
683
|
+
name
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* 解析表达式 - 需要在子类中实现
|
|
688
|
+
*/
|
|
689
|
+
parseExpression() {
|
|
690
|
+
throw new Error("parseExpression must be implemented in subclass");
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/core/parser/arrow-function-parser.ts
|
|
990
696
|
/**
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
* @template TResult - 表达式结果类型
|
|
994
|
-
* @param data - 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
995
|
-
* @param values - 变量值映射,按变量名提供值
|
|
996
|
-
* @returns 最后一个表达式的求值结果
|
|
997
|
-
*
|
|
998
|
-
* @throws 如果运行时类型验证失败或表达式执行出错
|
|
999
|
-
*
|
|
1000
|
-
* @example
|
|
1001
|
-
* ```ts
|
|
1002
|
-
* const compiled = [["x", "y"], "$[0]+$[1]", "$[1]*2"]
|
|
1003
|
-
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
|
|
1004
|
-
* // => 6 (3 * 2)
|
|
1005
|
-
* ```
|
|
697
|
+
* ArrowFunctionParser 类 - 继承 PrimaryParser,添加箭头函数解析功能
|
|
1006
698
|
*/
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
699
|
+
var ArrowFunctionParser = class extends PrimaryParser {
|
|
700
|
+
/**
|
|
701
|
+
* 尝试解析带括号的箭头函数: (a, b) => expr
|
|
702
|
+
* 使用回溯机制
|
|
703
|
+
*/
|
|
704
|
+
tryParseArrowFunction() {
|
|
705
|
+
const savedPos = this.pos;
|
|
706
|
+
try {
|
|
707
|
+
this.expect("(");
|
|
708
|
+
this.skipWhitespace();
|
|
709
|
+
const params = [];
|
|
710
|
+
while (this.peek() !== ")") {
|
|
711
|
+
if (!isIdentifierStart(this.peek())) throw new Error("Expected identifier");
|
|
712
|
+
params.push(this.parseIdentifier());
|
|
713
|
+
this.skipWhitespace();
|
|
714
|
+
if (this.peek() === ",") {
|
|
715
|
+
this.advance();
|
|
716
|
+
this.skipWhitespace();
|
|
717
|
+
} else break;
|
|
718
|
+
}
|
|
719
|
+
this.expect(")");
|
|
720
|
+
this.skipWhitespace();
|
|
721
|
+
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
722
|
+
this.pos += 2;
|
|
723
|
+
this.skipWhitespace();
|
|
724
|
+
return {
|
|
725
|
+
type: "ArrowFunctionExpr",
|
|
726
|
+
params,
|
|
727
|
+
body: this.parseExpression()
|
|
728
|
+
};
|
|
729
|
+
} catch {
|
|
730
|
+
this.pos = savedPos;
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
1023
733
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
734
|
+
/**
|
|
735
|
+
* 尝试解析单参数无括号的箭头函数: a => expr
|
|
736
|
+
* 使用回溯机制
|
|
737
|
+
*/
|
|
738
|
+
tryParseSingleParamArrowFunction() {
|
|
739
|
+
const savedPos = this.pos;
|
|
740
|
+
try {
|
|
741
|
+
const param = this.parseIdentifier();
|
|
742
|
+
this.skipWhitespace();
|
|
743
|
+
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
744
|
+
this.pos += 2;
|
|
745
|
+
this.skipWhitespace();
|
|
746
|
+
const body = this.parseExpression();
|
|
747
|
+
return {
|
|
748
|
+
type: "ArrowFunctionExpr",
|
|
749
|
+
params: [param],
|
|
750
|
+
body
|
|
751
|
+
};
|
|
752
|
+
} catch {
|
|
753
|
+
this.pos = savedPos;
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
1028
756
|
}
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* 构造求值函数体
|
|
1032
|
-
*
|
|
1033
|
-
* @param expressions - 表达式列表(可包含控制流节点和 FnNode)
|
|
1034
|
-
* @param variableCount - 变量数量
|
|
1035
|
-
* @returns 函数体字符串
|
|
1036
|
-
*/
|
|
1037
|
-
function buildEvaluatorFunctionBody(expressions, variableCount) {
|
|
1038
|
-
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
1039
|
-
return translateControlFlow(expressions, variableCount);
|
|
1040
|
-
}
|
|
757
|
+
};
|
|
1041
758
|
|
|
1042
759
|
//#endregion
|
|
1043
|
-
//#region src/parser.ts
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
this.skipWhitespace();
|
|
1052
|
-
const node = this.parseExpression();
|
|
1053
|
-
this.skipWhitespace();
|
|
1054
|
-
if (this.pos < this.source.length) throw new Error(`Unexpected token at position ${this.pos}: ${this.source.slice(this.pos, this.pos + 10)}`);
|
|
1055
|
-
return node;
|
|
1056
|
-
}
|
|
760
|
+
//#region src/core/parser/expression-parser.ts
|
|
761
|
+
/**
|
|
762
|
+
* ExpressionParser 类 - 继承 ArrowFunctionParser,实现完整的表达式解析功能
|
|
763
|
+
*/
|
|
764
|
+
var ExpressionParser = class extends ArrowFunctionParser {
|
|
765
|
+
/**
|
|
766
|
+
* 解析表达式的入口
|
|
767
|
+
*/
|
|
1057
768
|
parseExpression() {
|
|
1058
769
|
return this.parseConditional();
|
|
1059
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* 解析条件(三元)表达式: test ? consequent : alternate
|
|
773
|
+
*/
|
|
1060
774
|
parseConditional() {
|
|
1061
775
|
let node = this.parseBinary(0);
|
|
1062
776
|
this.skipWhitespace();
|
|
@@ -1077,6 +791,9 @@ var Parser = class Parser {
|
|
|
1077
791
|
}
|
|
1078
792
|
return node;
|
|
1079
793
|
}
|
|
794
|
+
/**
|
|
795
|
+
* 解析二元表达式,使用优先级爬升算法
|
|
796
|
+
*/
|
|
1080
797
|
parseBinary(minPrec) {
|
|
1081
798
|
let left = this.parseUnary();
|
|
1082
799
|
while (true) {
|
|
@@ -1096,6 +813,9 @@ var Parser = class Parser {
|
|
|
1096
813
|
}
|
|
1097
814
|
return left;
|
|
1098
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* 解析一元表达式
|
|
818
|
+
*/
|
|
1099
819
|
parseUnary() {
|
|
1100
820
|
this.skipWhitespace();
|
|
1101
821
|
const ch = this.peek();
|
|
@@ -1120,6 +840,9 @@ var Parser = class Parser {
|
|
|
1120
840
|
}
|
|
1121
841
|
return this.parsePostfix();
|
|
1122
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* 解析后缀表达式(成员访问、函数调用、可选链等)
|
|
845
|
+
*/
|
|
1123
846
|
parsePostfix() {
|
|
1124
847
|
let node = this.parsePrimary();
|
|
1125
848
|
while (true) {
|
|
@@ -1200,10 +923,13 @@ var Parser = class Parser {
|
|
|
1200
923
|
}
|
|
1201
924
|
return node;
|
|
1202
925
|
}
|
|
926
|
+
/**
|
|
927
|
+
* 解析基本表达式(字面量、标识符、括号表达式、箭头函数等)
|
|
928
|
+
*/
|
|
1203
929
|
parsePrimary() {
|
|
1204
930
|
this.skipWhitespace();
|
|
1205
931
|
const ch = this.peek();
|
|
1206
|
-
if (
|
|
932
|
+
if (isDigit(ch) || ch === "." && isDigit(this.peekAt(1))) return this.parseNumber();
|
|
1207
933
|
if (ch === "\"" || ch === "'" || ch === "`") return this.parseString();
|
|
1208
934
|
if (ch === "[") return this.parseArray();
|
|
1209
935
|
if (ch === "{") return this.parseObject();
|
|
@@ -1230,316 +956,355 @@ var Parser = class Parser {
|
|
|
1230
956
|
type: "Identifier",
|
|
1231
957
|
name: "undefined"
|
|
1232
958
|
};
|
|
1233
|
-
if (
|
|
959
|
+
if (isIdentifierStart(ch)) {
|
|
1234
960
|
const arrowFunc = this.tryParseSingleParamArrowFunction();
|
|
1235
961
|
if (arrowFunc) return arrowFunc;
|
|
1236
962
|
return this.parseIdentifier();
|
|
1237
963
|
}
|
|
1238
964
|
throw new Error(`Unexpected character at position ${this.pos}: ${ch}`);
|
|
1239
965
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
this.advance();
|
|
1246
|
-
this.advance();
|
|
1247
|
-
while (this.isHexDigit(this.peek())) this.advance();
|
|
1248
|
-
const raw = this.source.slice(start, this.pos);
|
|
1249
|
-
return {
|
|
1250
|
-
type: "NumberLiteral",
|
|
1251
|
-
value: Number(raw),
|
|
1252
|
-
raw
|
|
1253
|
-
};
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
while (this.isDigit(this.peek())) this.advance();
|
|
1257
|
-
if (this.peek() === "." && this.isDigit(this.peekAt(1))) {
|
|
1258
|
-
this.advance();
|
|
1259
|
-
while (this.isDigit(this.peek())) this.advance();
|
|
1260
|
-
}
|
|
1261
|
-
if (this.peek()?.toLowerCase() === "e") {
|
|
1262
|
-
this.advance();
|
|
1263
|
-
if (this.peek() === "+" || this.peek() === "-") this.advance();
|
|
1264
|
-
while (this.isDigit(this.peek())) this.advance();
|
|
1265
|
-
}
|
|
1266
|
-
const raw = this.source.slice(start, this.pos);
|
|
1267
|
-
return {
|
|
1268
|
-
type: "NumberLiteral",
|
|
1269
|
-
value: Number(raw),
|
|
1270
|
-
raw
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
static ESCAPE_CHARS = {
|
|
1274
|
-
n: "\n",
|
|
1275
|
-
r: "\r",
|
|
1276
|
-
t: " ",
|
|
1277
|
-
"\\": "\\",
|
|
1278
|
-
"'": "'",
|
|
1279
|
-
"\"": "\"",
|
|
1280
|
-
"`": "`"
|
|
1281
|
-
};
|
|
1282
|
-
parseString() {
|
|
1283
|
-
const quote = this.peek();
|
|
1284
|
-
this.advance();
|
|
1285
|
-
let value = "";
|
|
1286
|
-
while (this.pos < this.source.length && this.peek() !== quote) if (this.peek() === "\\") {
|
|
1287
|
-
this.advance();
|
|
1288
|
-
const escaped = this.peek();
|
|
1289
|
-
value += Parser.ESCAPE_CHARS[escaped] ?? escaped;
|
|
1290
|
-
this.advance();
|
|
1291
|
-
} else {
|
|
1292
|
-
value += this.peek();
|
|
1293
|
-
this.advance();
|
|
1294
|
-
}
|
|
1295
|
-
this.expect(quote);
|
|
1296
|
-
return {
|
|
1297
|
-
type: "StringLiteral",
|
|
1298
|
-
value,
|
|
1299
|
-
quote
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
parseArray() {
|
|
1303
|
-
this.expect("[");
|
|
1304
|
-
const elements = [];
|
|
966
|
+
/**
|
|
967
|
+
* 解析函数调用参数列表
|
|
968
|
+
*/
|
|
969
|
+
parseArguments() {
|
|
970
|
+
const args = [];
|
|
1305
971
|
this.skipWhitespace();
|
|
1306
|
-
while (this.peek() !== "
|
|
1307
|
-
|
|
972
|
+
while (this.peek() !== ")") {
|
|
973
|
+
args.push(this.parseExpression());
|
|
1308
974
|
this.skipWhitespace();
|
|
1309
975
|
if (this.peek() === ",") {
|
|
1310
976
|
this.advance();
|
|
1311
977
|
this.skipWhitespace();
|
|
1312
978
|
} else break;
|
|
1313
979
|
}
|
|
1314
|
-
|
|
1315
|
-
return {
|
|
1316
|
-
type: "ArrayExpr",
|
|
1317
|
-
elements
|
|
1318
|
-
};
|
|
980
|
+
return args;
|
|
1319
981
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
//#endregion
|
|
985
|
+
//#region src/core/parser/parser.ts
|
|
986
|
+
/**
|
|
987
|
+
* Parser 类 - 完整的 JavaScript 表达式解析器
|
|
988
|
+
*/
|
|
989
|
+
var Parser = class extends ExpressionParser {
|
|
990
|
+
/**
|
|
991
|
+
* 解析表达式字符串为 AST
|
|
992
|
+
*/
|
|
993
|
+
parse() {
|
|
1323
994
|
this.skipWhitespace();
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
995
|
+
const node = this.parseExpression();
|
|
996
|
+
this.skipWhitespace();
|
|
997
|
+
if (this.pos < this.source.length) throw new Error(`Unexpected token at position ${this.pos}: ${this.source.slice(this.pos, this.pos + 10)}`);
|
|
998
|
+
return node;
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
//#endregion
|
|
1003
|
+
//#region src/core/parser/index.ts
|
|
1004
|
+
/**
|
|
1005
|
+
* 解析 JavaScript 表达式为 AST
|
|
1006
|
+
*/
|
|
1007
|
+
function parse(source) {
|
|
1008
|
+
return new Parser(source).parse();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
//#endregion
|
|
1012
|
+
//#region src/proxy/proxy-metadata.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* 全局 WeakMap 存储
|
|
1015
|
+
*/
|
|
1016
|
+
const proxyMetadata = /* @__PURE__ */ new WeakMap();
|
|
1017
|
+
/**
|
|
1018
|
+
* 设置 Proxy 元数据
|
|
1019
|
+
*/
|
|
1020
|
+
function setProxyMetadata(proxy, metadata) {
|
|
1021
|
+
proxyMetadata.set(proxy, metadata);
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* 获取 Proxy 元数据
|
|
1025
|
+
*/
|
|
1026
|
+
function getProxyMetadata(proxy) {
|
|
1027
|
+
return proxyMetadata.get(proxy);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
//#endregion
|
|
1031
|
+
//#region src/proxy/proxy-variable.ts
|
|
1032
|
+
const typedArrayConstructors = [
|
|
1033
|
+
"Int8Array",
|
|
1034
|
+
"Uint8Array",
|
|
1035
|
+
"Uint8ClampedArray",
|
|
1036
|
+
"Int16Array",
|
|
1037
|
+
"Uint16Array",
|
|
1038
|
+
"Int32Array",
|
|
1039
|
+
"Uint32Array",
|
|
1040
|
+
"Float32Array",
|
|
1041
|
+
"Float64Array",
|
|
1042
|
+
"BigInt64Array",
|
|
1043
|
+
"BigUint64Array"
|
|
1044
|
+
];
|
|
1045
|
+
/**
|
|
1046
|
+
* 创建占位符 AST 节点
|
|
1047
|
+
*/
|
|
1048
|
+
function placeholder(id) {
|
|
1049
|
+
return {
|
|
1050
|
+
type: "Placeholder",
|
|
1051
|
+
id
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* 创建标识符 AST 节点
|
|
1056
|
+
*/
|
|
1057
|
+
function identifier(name) {
|
|
1058
|
+
return {
|
|
1059
|
+
type: "Identifier",
|
|
1060
|
+
name
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* 创建数字字面量 AST 节点
|
|
1065
|
+
*/
|
|
1066
|
+
function numberLiteral(value) {
|
|
1067
|
+
return {
|
|
1068
|
+
type: "NumberLiteral",
|
|
1069
|
+
value,
|
|
1070
|
+
raw: String(value)
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* 创建字符串字面量 AST 节点
|
|
1075
|
+
*/
|
|
1076
|
+
function stringLiteral(value, quote = "\"") {
|
|
1077
|
+
return {
|
|
1078
|
+
type: "StringLiteral",
|
|
1079
|
+
value,
|
|
1080
|
+
quote
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* 创建成员表达式 AST 节点
|
|
1085
|
+
*/
|
|
1086
|
+
function memberExpr(object, property) {
|
|
1087
|
+
return {
|
|
1088
|
+
type: "MemberExpr",
|
|
1089
|
+
object,
|
|
1090
|
+
property,
|
|
1091
|
+
computed: false,
|
|
1092
|
+
optional: false
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* 创建调用表达式 AST 节点
|
|
1097
|
+
*/
|
|
1098
|
+
function callExpr(callee, arguments_) {
|
|
1099
|
+
return {
|
|
1100
|
+
type: "CallExpr",
|
|
1101
|
+
callee,
|
|
1102
|
+
arguments: arguments_,
|
|
1103
|
+
optional: false
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* 创建数组表达式 AST 节点
|
|
1108
|
+
*/
|
|
1109
|
+
function arrayExpr(elements) {
|
|
1110
|
+
return {
|
|
1111
|
+
type: "ArrayExpr",
|
|
1112
|
+
elements
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* 检查对象是否为 TypedArray 实例
|
|
1117
|
+
*/
|
|
1118
|
+
function getTypedArrayConstructor(value) {
|
|
1119
|
+
for (const constructorName of typedArrayConstructors) {
|
|
1120
|
+
const Constructor = globalThis[constructorName];
|
|
1121
|
+
if (Constructor && value instanceof Constructor) return Constructor;
|
|
1338
1122
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
this.skipWhitespace();
|
|
1356
|
-
const value = this.parseExpression();
|
|
1357
|
-
return {
|
|
1358
|
-
key,
|
|
1359
|
-
value,
|
|
1360
|
-
computed,
|
|
1361
|
-
shorthand: false
|
|
1362
|
-
};
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* 序列化参数为 AST 节点
|
|
1127
|
+
* - Proxy Variable/Expression:使用 ast 或占位符标识符
|
|
1128
|
+
* - 数组:返回 ArrayExpr 节点
|
|
1129
|
+
* - 对象:返回 ObjectExpr 节点
|
|
1130
|
+
* - 原始值:返回对应的字面量节点
|
|
1131
|
+
* - Date, RegExp, BigInt, URL, URLSearchParams, Map, Set, TypedArray, DataView: 构造函数调用
|
|
1132
|
+
*/
|
|
1133
|
+
function serializeArgumentToAST(arg) {
|
|
1134
|
+
if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
1135
|
+
const meta = getProxyMetadata(arg);
|
|
1136
|
+
if (meta) {
|
|
1137
|
+
if (meta.ast) return meta.ast;
|
|
1138
|
+
if (meta.rootVariable) return placeholder(meta.rootVariable);
|
|
1363
1139
|
}
|
|
1364
|
-
if (key.type !== "Identifier") throw new Error("Shorthand property must be an identifier");
|
|
1365
|
-
return {
|
|
1366
|
-
key,
|
|
1367
|
-
value: key,
|
|
1368
|
-
computed: false,
|
|
1369
|
-
shorthand: true
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
parseIdentifier() {
|
|
1373
|
-
const start = this.pos;
|
|
1374
|
-
while (this.isIdentifierPart(this.peek())) this.advance();
|
|
1375
|
-
const name = this.source.slice(start, this.pos);
|
|
1376
|
-
if (!name) throw new Error(`Expected identifier at position ${this.pos}`);
|
|
1377
|
-
return {
|
|
1378
|
-
type: "Identifier",
|
|
1379
|
-
name
|
|
1380
|
-
};
|
|
1381
1140
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
this.expect("(");
|
|
1390
|
-
this.skipWhitespace();
|
|
1391
|
-
const params = [];
|
|
1392
|
-
while (this.peek() !== ")") {
|
|
1393
|
-
if (!this.isIdentifierStart(this.peek())) throw new Error("Expected identifier");
|
|
1394
|
-
params.push(this.parseIdentifier());
|
|
1395
|
-
this.skipWhitespace();
|
|
1396
|
-
if (this.peek() === ",") {
|
|
1397
|
-
this.advance();
|
|
1398
|
-
this.skipWhitespace();
|
|
1399
|
-
} else break;
|
|
1400
|
-
}
|
|
1401
|
-
this.expect(")");
|
|
1402
|
-
this.skipWhitespace();
|
|
1403
|
-
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
1404
|
-
this.pos += 2;
|
|
1405
|
-
this.skipWhitespace();
|
|
1406
|
-
return {
|
|
1407
|
-
type: "ArrowFunctionExpr",
|
|
1408
|
-
params,
|
|
1409
|
-
body: this.parseExpression()
|
|
1410
|
-
};
|
|
1411
|
-
} catch {
|
|
1412
|
-
this.pos = savedPos;
|
|
1413
|
-
return null;
|
|
1141
|
+
if (Array.isArray(arg)) return arrayExpr(arg.map(serializeArgumentToAST));
|
|
1142
|
+
if (typeof arg === "object" && arg !== null) {
|
|
1143
|
+
if (arg instanceof Date) return callExpr(identifier("Date"), [numberLiteral(arg.getTime())]);
|
|
1144
|
+
if (arg instanceof RegExp) {
|
|
1145
|
+
const args = [stringLiteral(arg.source)];
|
|
1146
|
+
if (arg.flags) args.push(stringLiteral(arg.flags));
|
|
1147
|
+
return callExpr(identifier("RegExp"), args);
|
|
1414
1148
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
try {
|
|
1423
|
-
const param = this.parseIdentifier();
|
|
1424
|
-
this.skipWhitespace();
|
|
1425
|
-
if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
|
|
1426
|
-
this.pos += 2;
|
|
1427
|
-
this.skipWhitespace();
|
|
1428
|
-
const body = this.parseExpression();
|
|
1429
|
-
return {
|
|
1430
|
-
type: "ArrowFunctionExpr",
|
|
1431
|
-
params: [param],
|
|
1432
|
-
body
|
|
1433
|
-
};
|
|
1434
|
-
} catch {
|
|
1435
|
-
this.pos = savedPos;
|
|
1436
|
-
return null;
|
|
1149
|
+
if (typeof URL !== "undefined" && arg instanceof URL) return callExpr(identifier("URL"), [stringLiteral(arg.href)]);
|
|
1150
|
+
if (typeof URLSearchParams !== "undefined" && arg instanceof URLSearchParams) {
|
|
1151
|
+
const entries = [];
|
|
1152
|
+
arg.forEach((value, key) => {
|
|
1153
|
+
entries.push(arrayExpr([stringLiteral(key), stringLiteral(value)]));
|
|
1154
|
+
});
|
|
1155
|
+
return callExpr(identifier("URLSearchParams"), [arrayExpr(entries)]);
|
|
1437
1156
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
this.skipWhitespace();
|
|
1445
|
-
if (this.peek() === ",") {
|
|
1446
|
-
this.advance();
|
|
1447
|
-
this.skipWhitespace();
|
|
1448
|
-
} else break;
|
|
1157
|
+
if (arg instanceof Map) {
|
|
1158
|
+
const entries = [];
|
|
1159
|
+
arg.forEach((value, key) => {
|
|
1160
|
+
entries.push(arrayExpr([serializeArgumentToAST(key), serializeArgumentToAST(value)]));
|
|
1161
|
+
});
|
|
1162
|
+
return callExpr(identifier("Map"), [arrayExpr(entries)]);
|
|
1449
1163
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
">>>",
|
|
1455
|
-
"===",
|
|
1456
|
-
"!==",
|
|
1457
|
-
"&&",
|
|
1458
|
-
"||",
|
|
1459
|
-
"??",
|
|
1460
|
-
"==",
|
|
1461
|
-
"!=",
|
|
1462
|
-
"<=",
|
|
1463
|
-
">=",
|
|
1464
|
-
"<<",
|
|
1465
|
-
">>",
|
|
1466
|
-
"**",
|
|
1467
|
-
"in",
|
|
1468
|
-
"+",
|
|
1469
|
-
"-",
|
|
1470
|
-
"*",
|
|
1471
|
-
"/",
|
|
1472
|
-
"%",
|
|
1473
|
-
"<",
|
|
1474
|
-
">",
|
|
1475
|
-
"&",
|
|
1476
|
-
"|",
|
|
1477
|
-
"^"
|
|
1478
|
-
];
|
|
1479
|
-
static KEYWORD_OPERATORS = new Set(["in", "instanceof"]);
|
|
1480
|
-
peekOperator() {
|
|
1481
|
-
for (const op of Parser.OPERATORS) {
|
|
1482
|
-
if (!this.source.startsWith(op, this.pos)) continue;
|
|
1483
|
-
if (Parser.KEYWORD_OPERATORS.has(op)) {
|
|
1484
|
-
const nextChar = this.source[this.pos + op.length];
|
|
1485
|
-
if (nextChar && this.isIdentifierPart(nextChar)) continue;
|
|
1486
|
-
}
|
|
1487
|
-
return op;
|
|
1164
|
+
if (arg instanceof Set) {
|
|
1165
|
+
const values = [];
|
|
1166
|
+
arg.forEach((value) => values.push(serializeArgumentToAST(value)));
|
|
1167
|
+
return callExpr(identifier("Set"), [arrayExpr(values)]);
|
|
1488
1168
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
if (!nextChar || !this.isIdentifierPart(nextChar)) {
|
|
1495
|
-
this.pos += keyword.length;
|
|
1496
|
-
return true;
|
|
1497
|
-
}
|
|
1169
|
+
const typedArrayConstructor = getTypedArrayConstructor(arg);
|
|
1170
|
+
if (typedArrayConstructor) {
|
|
1171
|
+
const values = [...arg].map(serializeArgumentToAST);
|
|
1172
|
+
const constructorName = typedArrayConstructor.name;
|
|
1173
|
+
return callExpr(identifier(constructorName), [arrayExpr(values)]);
|
|
1498
1174
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
return
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
}
|
|
1517
|
-
isDigit(ch) {
|
|
1518
|
-
const code = ch.charCodeAt(0);
|
|
1519
|
-
return code >= 48 && code <= 57;
|
|
1520
|
-
}
|
|
1521
|
-
isHexDigit(ch) {
|
|
1522
|
-
const code = ch.charCodeAt(0);
|
|
1523
|
-
return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
|
1524
|
-
}
|
|
1525
|
-
isIdentifierStart(ch) {
|
|
1526
|
-
const code = ch.charCodeAt(0);
|
|
1527
|
-
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95 || code === 36;
|
|
1175
|
+
if (arg instanceof ArrayBuffer) {
|
|
1176
|
+
const uint8Array = new Uint8Array(arg);
|
|
1177
|
+
const values = Array.from(uint8Array).map(numberLiteral);
|
|
1178
|
+
return memberExpr(callExpr(identifier("Uint8Array"), [arrayExpr(values)]), identifier("buffer"));
|
|
1179
|
+
}
|
|
1180
|
+
if (arg instanceof DataView) return callExpr(identifier("DataView"), [serializeArgumentToAST(arg.buffer)]);
|
|
1181
|
+
return {
|
|
1182
|
+
type: "ObjectExpr",
|
|
1183
|
+
properties: Object.entries(arg).map(([k, v]) => {
|
|
1184
|
+
return {
|
|
1185
|
+
key: /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? identifier(k) : stringLiteral(k),
|
|
1186
|
+
value: serializeArgumentToAST(v),
|
|
1187
|
+
computed: false,
|
|
1188
|
+
shorthand: false
|
|
1189
|
+
};
|
|
1190
|
+
})
|
|
1191
|
+
};
|
|
1528
1192
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1193
|
+
if (arg === null) return { type: "NullLiteral" };
|
|
1194
|
+
if (arg === void 0) return identifier("undefined");
|
|
1195
|
+
if (typeof arg === "boolean") return {
|
|
1196
|
+
type: "BooleanLiteral",
|
|
1197
|
+
value: arg
|
|
1198
|
+
};
|
|
1199
|
+
if (typeof arg === "number") return numberLiteral(arg);
|
|
1200
|
+
if (typeof arg === "string") return stringLiteral(arg);
|
|
1201
|
+
if (typeof arg === "bigint") return callExpr(identifier("BigInt"), [stringLiteral(arg.toString())]);
|
|
1202
|
+
throw new Error(`Unsupported argument type: ${typeof arg}`);
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* 从参数中收集依赖的 Symbol
|
|
1206
|
+
* 递归遍历数组和对象,收集所有 Proxy 的依赖
|
|
1207
|
+
*/
|
|
1208
|
+
function collectDepsFromArgs(args, deps) {
|
|
1209
|
+
for (const arg of args) if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
|
|
1210
|
+
const meta = getProxyMetadata(arg);
|
|
1211
|
+
if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
|
|
1212
|
+
else if (Array.isArray(arg)) collectDepsFromArgs(arg, deps);
|
|
1213
|
+
else if (typeof arg === "object") collectDepsFromArgs(Object.values(arg), deps);
|
|
1532
1214
|
}
|
|
1533
|
-
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* 创建 Proxy 的公共 handler
|
|
1218
|
+
*/
|
|
1219
|
+
function createProxyHandler(ast, deps) {
|
|
1220
|
+
return {
|
|
1221
|
+
get(_target, prop) {
|
|
1222
|
+
if (typeof prop === "symbol") return void 0;
|
|
1223
|
+
return createProxyExpressionWithAST(memberExpr(ast, identifier(String(prop))), deps);
|
|
1224
|
+
},
|
|
1225
|
+
apply(_target, _thisArg, args) {
|
|
1226
|
+
const callAst = callExpr(ast, args.map(serializeArgumentToAST));
|
|
1227
|
+
const newDeps = new Set(deps);
|
|
1228
|
+
collectDepsFromArgs(args, newDeps);
|
|
1229
|
+
return createProxyExpressionWithAST(callAst, newDeps);
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1534
1233
|
/**
|
|
1535
|
-
*
|
|
1234
|
+
* 创建根 Variable Proxy
|
|
1235
|
+
* 拦截属性访问,返回新的 expression proxy
|
|
1236
|
+
* 不可直接调用(apply 应该只在链式调用后可用)
|
|
1237
|
+
*
|
|
1238
|
+
* @param id - 变量的唯一标识 Symbol
|
|
1239
|
+
* @returns Proxy 包装的 Variable
|
|
1536
1240
|
*/
|
|
1537
|
-
function
|
|
1538
|
-
|
|
1241
|
+
function createProxyVariable(id) {
|
|
1242
|
+
const deps = new Set([id]);
|
|
1243
|
+
const ast = placeholder(id);
|
|
1244
|
+
const proxy = new Proxy(function() {}, createProxyHandler(ast, deps));
|
|
1245
|
+
setProxyMetadata(proxy, {
|
|
1246
|
+
type: "variable",
|
|
1247
|
+
rootVariable: id,
|
|
1248
|
+
dependencies: deps
|
|
1249
|
+
});
|
|
1250
|
+
return proxy;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* 创建带完整 AST 的 Proxy(方法调用后)
|
|
1254
|
+
* 可以继续链式访问和调用
|
|
1255
|
+
*
|
|
1256
|
+
* @param ast - 完整的表达式 AST
|
|
1257
|
+
* @param deps - 依赖集合
|
|
1258
|
+
* @returns Proxy 包装的 Expression
|
|
1259
|
+
*/
|
|
1260
|
+
function createProxyExpressionWithAST(ast, deps, deferredAsts) {
|
|
1261
|
+
const proxy = new Proxy(function() {}, createProxyHandler(ast, deps));
|
|
1262
|
+
setProxyMetadata(proxy, {
|
|
1263
|
+
type: "expression",
|
|
1264
|
+
ast,
|
|
1265
|
+
dependencies: deps,
|
|
1266
|
+
deferredAsts
|
|
1267
|
+
});
|
|
1268
|
+
return proxy;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
//#endregion
|
|
1272
|
+
//#region src/api/variable.ts
|
|
1273
|
+
/**
|
|
1274
|
+
* 跟踪每个 variable 的唯一 Symbol ID
|
|
1275
|
+
*/
|
|
1276
|
+
const variableIds = /* @__PURE__ */ new WeakMap();
|
|
1277
|
+
/**
|
|
1278
|
+
* 计数器用于生成唯一变量 ID
|
|
1279
|
+
*/
|
|
1280
|
+
let variableCounter = 0;
|
|
1281
|
+
/**
|
|
1282
|
+
* 创建一个类型化变量
|
|
1283
|
+
* 返回 Proxy 对象,支持链式属性访问和方法调用
|
|
1284
|
+
*
|
|
1285
|
+
* @example
|
|
1286
|
+
* ```ts
|
|
1287
|
+
* const x = variable<number>();
|
|
1288
|
+
* const config = variable<{ timeout: number }>();
|
|
1289
|
+
* const timeout = config.timeout; // Proxy expression
|
|
1290
|
+
* ```
|
|
1291
|
+
*/
|
|
1292
|
+
function variable() {
|
|
1293
|
+
const id = Symbol(`var_${variableCounter++}`);
|
|
1294
|
+
const proxy = createProxyVariable(id);
|
|
1295
|
+
variableIds.set(proxy, id);
|
|
1296
|
+
return proxy;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* 获取 variable 的唯一 Symbol ID
|
|
1300
|
+
*/
|
|
1301
|
+
function getVariableId(variable) {
|
|
1302
|
+
if (typeof variable !== "object" && typeof variable !== "function" || variable === null) return void 0;
|
|
1303
|
+
return variableIds.get(variable);
|
|
1539
1304
|
}
|
|
1540
1305
|
|
|
1541
1306
|
//#endregion
|
|
1542
|
-
//#region src/expr.ts
|
|
1307
|
+
//#region src/api/expr.ts
|
|
1543
1308
|
/**
|
|
1544
1309
|
* 创建表达式
|
|
1545
1310
|
* 返回 Proxy Expression,可以继续链式调用
|
|
@@ -1591,236 +1356,690 @@ function expr(context) {
|
|
|
1591
1356
|
const meta = getProxyMetadata(value);
|
|
1592
1357
|
if (meta?.ast) nameToExprAST.set(name, meta.ast);
|
|
1593
1358
|
}
|
|
1594
|
-
|
|
1359
|
+
const ast = parse(source);
|
|
1360
|
+
const refCounts = /* @__PURE__ */ new Map();
|
|
1361
|
+
countIdentifierReferences(ast, refCounts);
|
|
1362
|
+
const deferredAsts = /* @__PURE__ */ new Map();
|
|
1363
|
+
return createProxyExpressionWithAST(transformIdentifiers(ast, (name) => {
|
|
1595
1364
|
const id = nameToId.get(name);
|
|
1596
1365
|
if (id) return {
|
|
1597
1366
|
type: "Placeholder",
|
|
1598
1367
|
id
|
|
1599
1368
|
};
|
|
1600
1369
|
const exprAST = nameToExprAST.get(name);
|
|
1601
|
-
if (exprAST)
|
|
1370
|
+
if (exprAST) {
|
|
1371
|
+
if ((refCounts.get(name) ?? 0) <= 1) return exprAST;
|
|
1372
|
+
deferredAsts.set(name, exprAST);
|
|
1373
|
+
return name;
|
|
1374
|
+
}
|
|
1602
1375
|
return name;
|
|
1603
|
-
}), deps);
|
|
1376
|
+
}), deps, deferredAsts.size > 0 ? deferredAsts : void 0);
|
|
1604
1377
|
};
|
|
1605
1378
|
}
|
|
1606
1379
|
|
|
1607
1380
|
//#endregion
|
|
1608
|
-
//#region src/lambda.ts
|
|
1381
|
+
//#region src/api/lambda.ts
|
|
1609
1382
|
/**
|
|
1610
1383
|
* Lambda 参数计数器,用于生成唯一 ID
|
|
1611
1384
|
*/
|
|
1612
1385
|
let lambdaParamCounter = 0;
|
|
1613
1386
|
/**
|
|
1614
|
-
* Lambda 参数到索引的映射
|
|
1615
|
-
* 用于编译时确定参数位置
|
|
1387
|
+
* Lambda 参数到索引的映射
|
|
1388
|
+
* 用于编译时确定参数位置
|
|
1389
|
+
*/
|
|
1390
|
+
const lambdaParamIndices = /* @__PURE__ */ new WeakMap();
|
|
1391
|
+
/**
|
|
1392
|
+
* 创建 lambda 参数代理
|
|
1393
|
+
* 生成带特殊标记的 Proxy,用于在表达式中追踪参数
|
|
1394
|
+
*
|
|
1395
|
+
* @param index - 参数索引(0, 1, 2...)
|
|
1396
|
+
* @returns Lambda 参数代理
|
|
1397
|
+
*/
|
|
1398
|
+
function createLambdaParam(index) {
|
|
1399
|
+
const proxy = createProxyVariable(Symbol(`lambda_param_${lambdaParamCounter++}_${index}`));
|
|
1400
|
+
lambdaParamIndices.set(proxy, index);
|
|
1401
|
+
return proxy;
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* 创建类型安全的 lambda 表达式
|
|
1405
|
+
*
|
|
1406
|
+
* @template Args - 参数类型元组
|
|
1407
|
+
* @template R - 返回值类型
|
|
1408
|
+
* @param builder - Lambda 构建函数,接收参数代理,返回函数体表达式
|
|
1409
|
+
* @returns Lambda 表达式代理
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* ```ts
|
|
1413
|
+
* const add = lambda<[number, number], number>(
|
|
1414
|
+
* (a, b) => expr({ a, b })("a + b")
|
|
1415
|
+
* );
|
|
1416
|
+
*
|
|
1417
|
+
* const numbers = variable<number[]>();
|
|
1418
|
+
* const sum = numbers.reduce(add, 0);
|
|
1419
|
+
* ```
|
|
1420
|
+
*/
|
|
1421
|
+
function lambda(builder) {
|
|
1422
|
+
const paramCount = builder.length;
|
|
1423
|
+
const { params, paramSymbols } = createLambdaParams(paramCount);
|
|
1424
|
+
const { bodyAst, bodyDeps } = extractBodyAstAndDeps(builder(...params));
|
|
1425
|
+
const lambdaProxy = createProxyExpressionWithAST(createArrowFunctionAst(bodyAst, paramCount, paramSymbols), filterClosureDeps(bodyDeps, paramSymbols));
|
|
1426
|
+
const existingMeta = getProxyMetadata(lambdaProxy);
|
|
1427
|
+
if (existingMeta) setProxyMetadata(lambdaProxy, {
|
|
1428
|
+
...existingMeta,
|
|
1429
|
+
type: "expression"
|
|
1430
|
+
});
|
|
1431
|
+
return lambdaProxy;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* 创建 lambda 参数和符号映射
|
|
1435
|
+
*/
|
|
1436
|
+
function createLambdaParams(paramCount) {
|
|
1437
|
+
const params = [];
|
|
1438
|
+
const paramSymbols = [];
|
|
1439
|
+
for (let i = 0; i < paramCount; i++) {
|
|
1440
|
+
const param = createLambdaParam(i);
|
|
1441
|
+
params.push(param);
|
|
1442
|
+
const meta = getProxyMetadata(param);
|
|
1443
|
+
if (meta?.rootVariable) paramSymbols.push(meta.rootVariable);
|
|
1444
|
+
}
|
|
1445
|
+
return {
|
|
1446
|
+
params,
|
|
1447
|
+
paramSymbols
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* 从表达式中提取 AST 和依赖
|
|
1452
|
+
*/
|
|
1453
|
+
function extractBodyAstAndDeps(bodyExpr) {
|
|
1454
|
+
const meta = (typeof bodyExpr === "object" || typeof bodyExpr === "function") && bodyExpr !== null ? getProxyMetadata(bodyExpr) : void 0;
|
|
1455
|
+
if (meta?.ast) return {
|
|
1456
|
+
bodyAst: meta.ast,
|
|
1457
|
+
bodyDeps: meta.dependencies ?? /* @__PURE__ */ new Set()
|
|
1458
|
+
};
|
|
1459
|
+
else {
|
|
1460
|
+
const bodyDeps = /* @__PURE__ */ new Set();
|
|
1461
|
+
collectDepsFromArgs([bodyExpr], bodyDeps);
|
|
1462
|
+
return {
|
|
1463
|
+
bodyAst: serializeArgumentToAST(bodyExpr),
|
|
1464
|
+
bodyDeps
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* 创建箭头函数 AST
|
|
1470
|
+
* 使用 Placeholder 节点作为参数,在代码生成时再分配实际参数名
|
|
1471
|
+
*/
|
|
1472
|
+
function createArrowFunctionAst(bodyAst, paramCount, paramSymbols) {
|
|
1473
|
+
return {
|
|
1474
|
+
type: "ArrowFunctionExpr",
|
|
1475
|
+
params: Array.from({ length: paramCount }, (_, i) => ({
|
|
1476
|
+
type: "Placeholder",
|
|
1477
|
+
id: paramSymbols[i]
|
|
1478
|
+
})),
|
|
1479
|
+
body: bodyAst
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* 过滤掉 lambda 参数依赖,只保留外部闭包变量
|
|
1484
|
+
*/
|
|
1485
|
+
function filterClosureDeps(bodyDeps, paramSymbols) {
|
|
1486
|
+
const closureDeps = /* @__PURE__ */ new Set();
|
|
1487
|
+
for (const dep of bodyDeps) if (!paramSymbols.includes(dep)) closureDeps.add(dep);
|
|
1488
|
+
return closureDeps;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/api/template.ts
|
|
1493
|
+
/**
|
|
1494
|
+
* Tagged template 函数,用于创建包含变量的字符串表达式
|
|
1495
|
+
*
|
|
1496
|
+
* @example
|
|
1497
|
+
* ```ts
|
|
1498
|
+
* const name = variable<string>();
|
|
1499
|
+
* const count = variable<number>();
|
|
1500
|
+
*
|
|
1501
|
+
* const greeting = t`Hello, ${name}!`;
|
|
1502
|
+
* const message = t`You have ${count} items.`;
|
|
1503
|
+
*
|
|
1504
|
+
* const compiled = compile(greeting, { name });
|
|
1505
|
+
* const result = evaluate(compiled, { name: "Alice" }); // => "Hello, Alice!"
|
|
1506
|
+
* ```
|
|
1507
|
+
*/
|
|
1508
|
+
function t(strings, ...values) {
|
|
1509
|
+
const deps = /* @__PURE__ */ new Set();
|
|
1510
|
+
collectDepsFromArgs(values, deps);
|
|
1511
|
+
const parts = [];
|
|
1512
|
+
for (let i = 0; i < strings.length; i++) {
|
|
1513
|
+
const str = strings[i];
|
|
1514
|
+
if (str.length > 0) parts.push({
|
|
1515
|
+
type: "StringLiteral",
|
|
1516
|
+
value: str,
|
|
1517
|
+
quote: "\""
|
|
1518
|
+
});
|
|
1519
|
+
if (i < values.length) {
|
|
1520
|
+
const ast = serializeArgumentToAST(values[i]);
|
|
1521
|
+
parts.push(ast);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (parts.length === 0) return createProxyExpressionWithAST({
|
|
1525
|
+
type: "StringLiteral",
|
|
1526
|
+
value: "",
|
|
1527
|
+
quote: "\""
|
|
1528
|
+
}, deps);
|
|
1529
|
+
if (parts.length === 1) return createProxyExpressionWithAST(parts[0], deps);
|
|
1530
|
+
let resultAst = parts[0];
|
|
1531
|
+
for (let i = 1; i < parts.length; i++) resultAst = {
|
|
1532
|
+
type: "BinaryExpr",
|
|
1533
|
+
operator: "+",
|
|
1534
|
+
left: resultAst,
|
|
1535
|
+
right: parts[i]
|
|
1536
|
+
};
|
|
1537
|
+
return createProxyExpressionWithAST(resultAst, deps);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
//#endregion
|
|
1541
|
+
//#region src/api/wrap.ts
|
|
1542
|
+
/**
|
|
1543
|
+
* 将静态值包装为 Proxy Expression
|
|
1544
|
+
* 返回的 Proxy 可以像 Variable 一样调用方法和访问属性
|
|
1545
|
+
*
|
|
1546
|
+
* @template T - 值的类型
|
|
1547
|
+
* @param value - 要包装的静态值(支持原始值、对象、数组、Date、RegExp 等)
|
|
1548
|
+
* @returns Proxy Expression,可以继续链式调用
|
|
1549
|
+
*
|
|
1550
|
+
* @example
|
|
1551
|
+
* ```ts
|
|
1552
|
+
* // 包装 RegExp
|
|
1553
|
+
* const pattern = wrap(/^[a-z]+$/i);
|
|
1554
|
+
* const input = variable<string>();
|
|
1555
|
+
* const result = pattern.match(input);
|
|
1556
|
+
*
|
|
1557
|
+
* // 包装 Date
|
|
1558
|
+
* const now = wrap(new Date());
|
|
1559
|
+
* const year = now.getFullYear();
|
|
1560
|
+
*
|
|
1561
|
+
* // 包装数组
|
|
1562
|
+
* const numbers = wrap([1, 2, 3, 4, 5]);
|
|
1563
|
+
* const doubled = numbers.map((x) => x * 2);
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
function wrap(value) {
|
|
1567
|
+
return createProxyExpressionWithAST(serializeArgumentToAST(value), /* @__PURE__ */ new Set());
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
//#endregion
|
|
1571
|
+
//#region src/core/compile.ts
|
|
1572
|
+
const ALLOWED_GLOBALS = new Set([
|
|
1573
|
+
"Math",
|
|
1574
|
+
"JSON",
|
|
1575
|
+
"Date",
|
|
1576
|
+
"RegExp",
|
|
1577
|
+
"Number",
|
|
1578
|
+
"String",
|
|
1579
|
+
"Boolean",
|
|
1580
|
+
"Array",
|
|
1581
|
+
"Object",
|
|
1582
|
+
"undefined",
|
|
1583
|
+
"NaN",
|
|
1584
|
+
"Infinity",
|
|
1585
|
+
"isNaN",
|
|
1586
|
+
"isFinite",
|
|
1587
|
+
"parseInt",
|
|
1588
|
+
"parseFloat",
|
|
1589
|
+
"BigInt",
|
|
1590
|
+
"URL",
|
|
1591
|
+
"URLSearchParams",
|
|
1592
|
+
"Map",
|
|
1593
|
+
"Set",
|
|
1594
|
+
"Int8Array",
|
|
1595
|
+
"Uint8Array",
|
|
1596
|
+
"Uint8ClampedArray",
|
|
1597
|
+
"Int16Array",
|
|
1598
|
+
"Uint16Array",
|
|
1599
|
+
"Int32Array",
|
|
1600
|
+
"Uint32Array",
|
|
1601
|
+
"Float32Array",
|
|
1602
|
+
"Float64Array",
|
|
1603
|
+
"BigInt64Array",
|
|
1604
|
+
"BigUint64Array",
|
|
1605
|
+
"ArrayBuffer",
|
|
1606
|
+
"DataView"
|
|
1607
|
+
]);
|
|
1608
|
+
/**
|
|
1609
|
+
* 短路运算符对应的分支条件
|
|
1616
1610
|
*/
|
|
1617
|
-
const
|
|
1611
|
+
const BRANCH_CONDITIONS = {
|
|
1612
|
+
"||": (idx) => `$[${idx}]`,
|
|
1613
|
+
"&&": (idx) => `!$[${idx}]`,
|
|
1614
|
+
"??": (idx) => `$[${idx}]!=null`
|
|
1615
|
+
};
|
|
1618
1616
|
/**
|
|
1619
|
-
*
|
|
1620
|
-
* 生成带特殊标记的 Proxy,用于在表达式中追踪参数
|
|
1617
|
+
* 将 Proxy Expression 编译为可序列化的 JSON 结构
|
|
1621
1618
|
*
|
|
1622
|
-
* @
|
|
1623
|
-
* @
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
lambdaParamIndices.set(proxy, index);
|
|
1628
|
-
return proxy;
|
|
1629
|
-
}
|
|
1630
|
-
/**
|
|
1631
|
-
* 创建类型安全的 lambda 表达式
|
|
1619
|
+
* @template TResult - 表达式结果类型
|
|
1620
|
+
* @param expression - Proxy Expression,或包含 Proxy 的对象/数组/原始值
|
|
1621
|
+
* @param variables - 所有使用的变量定义
|
|
1622
|
+
* @param options - 编译选项
|
|
1623
|
+
* @returns 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
1632
1624
|
*
|
|
1633
|
-
* @
|
|
1634
|
-
* @template R - 返回值类型
|
|
1635
|
-
* @param builder - Lambda 构建函数,接收参数代理,返回函数体表达式
|
|
1636
|
-
* @returns Lambda 表达式代理
|
|
1625
|
+
* @throws 如果传入无效的表达式或未定义的变量引用
|
|
1637
1626
|
*
|
|
1638
1627
|
* @example
|
|
1639
1628
|
* ```ts
|
|
1640
|
-
* const
|
|
1641
|
-
*
|
|
1642
|
-
* )
|
|
1643
|
-
*
|
|
1644
|
-
* const
|
|
1645
|
-
*
|
|
1629
|
+
* const x = variable<number>()
|
|
1630
|
+
* const y = variable<number>()
|
|
1631
|
+
* const sum = expr({ x, y })("x + y")
|
|
1632
|
+
* const result = expr({ sum, x })("sum * x")
|
|
1633
|
+
* const compiled = compile(result, { x, y })
|
|
1634
|
+
* // => [["x", "y"], "($[0]+$[1])*$[0]"]
|
|
1646
1635
|
* ```
|
|
1647
1636
|
*/
|
|
1648
|
-
function
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
const
|
|
1652
|
-
const
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1637
|
+
function compile(expression, variables, _options = {}) {
|
|
1638
|
+
let deferredAsts;
|
|
1639
|
+
if ((typeof expression === "object" || typeof expression === "function") && expression !== null) deferredAsts = getProxyMetadata(expression)?.deferredAsts;
|
|
1640
|
+
const ast = serializeArgumentToAST(expression);
|
|
1641
|
+
const variableOrder = [];
|
|
1642
|
+
const variableToIndex = /* @__PURE__ */ new Map();
|
|
1643
|
+
const symbolToName = /* @__PURE__ */ new Map();
|
|
1644
|
+
for (const [name, value] of Object.entries(variables)) {
|
|
1645
|
+
if (!variableToIndex.has(name)) {
|
|
1646
|
+
const index = variableOrder.length;
|
|
1647
|
+
variableToIndex.set(name, index);
|
|
1648
|
+
variableOrder.push(name);
|
|
1649
|
+
}
|
|
1650
|
+
const id = getVariableId(value);
|
|
1651
|
+
if (id) symbolToName.set(id, name);
|
|
1652
|
+
}
|
|
1653
|
+
const topLevelExprs = [];
|
|
1654
|
+
const ctx = {
|
|
1655
|
+
nextParamIndex: 0,
|
|
1656
|
+
expressionStack: [topLevelExprs],
|
|
1657
|
+
nextIndex: variableOrder.length,
|
|
1658
|
+
variableCount: variableOrder.length
|
|
1659
|
+
};
|
|
1660
|
+
const deferredIndexMap = /* @__PURE__ */ new Map();
|
|
1661
|
+
if (deferredAsts && deferredAsts.size > 0) compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex, deferredIndexMap);
|
|
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;
|
|
1657
1667
|
});
|
|
1658
|
-
|
|
1668
|
+
const undefinedVars = [];
|
|
1669
|
+
const transformed = transformIdentifiers(placeholderTransformed, (name) => {
|
|
1670
|
+
if (/^\$\[\d+\]$/.test(name)) return name;
|
|
1671
|
+
if (/^_\d+$/.test(name)) return name;
|
|
1672
|
+
const index = variableToIndex.get(name);
|
|
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;
|
|
1678
|
+
});
|
|
1679
|
+
if (undefinedVars.length > 0) throw new Error(`Undefined variable(s): ${[...new Set(undefinedVars)].join(", ")}`);
|
|
1680
|
+
compileAst(transformed, ctx);
|
|
1681
|
+
return [variableOrder, ...topLevelExprs];
|
|
1659
1682
|
}
|
|
1660
1683
|
/**
|
|
1661
|
-
*
|
|
1684
|
+
* 获取当前表达式列表
|
|
1662
1685
|
*/
|
|
1663
|
-
function
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1686
|
+
function currentExprs(ctx) {
|
|
1687
|
+
return ctx.expressionStack[ctx.expressionStack.length - 1];
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* 编译推迟的子表达式(因引用计数 >1 未内联)
|
|
1691
|
+
* 使用递归处理,自动按依赖拓扑顺序编译
|
|
1692
|
+
*/
|
|
1693
|
+
function compileDeferredExprs(deferredAsts, ctx, symbolToName, variableToIndex, deferredIndexMap) {
|
|
1694
|
+
const processing = /* @__PURE__ */ new Set();
|
|
1695
|
+
function compileOne(name) {
|
|
1696
|
+
if (processing.has(name)) throw new Error(`Circular reference in deferred expressions: ${name}`);
|
|
1697
|
+
const existing = deferredIndexMap.get(name);
|
|
1698
|
+
if (existing !== void 0) return existing;
|
|
1699
|
+
const ast = deferredAsts.get(name);
|
|
1700
|
+
if (!ast) throw new Error(`Unknown deferred expression: ${name}`);
|
|
1701
|
+
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
|
+
const undefinedVars = [];
|
|
1709
|
+
const t2 = transformIdentifiers(t1, (n) => {
|
|
1710
|
+
if (/^\$\[\d+\]$/.test(n)) return n;
|
|
1711
|
+
if (/^_\d+$/.test(n)) return n;
|
|
1712
|
+
const varIdx = variableToIndex.get(n);
|
|
1713
|
+
if (varIdx !== void 0) return `$[${varIdx}]`;
|
|
1714
|
+
if (deferredAsts.has(n)) return `$[${compileOne(n)}]`;
|
|
1715
|
+
if (!ALLOWED_GLOBALS.has(n)) undefinedVars.push(n);
|
|
1716
|
+
return n;
|
|
1717
|
+
});
|
|
1718
|
+
if (undefinedVars.length > 0) throw new Error(`Undefined variable(s) in deferred expression "${name}": ${[...new Set(undefinedVars)].join(", ")}`);
|
|
1719
|
+
const idx = compileAst(t2, ctx);
|
|
1720
|
+
deferredIndexMap.set(name, idx);
|
|
1721
|
+
processing.delete(name);
|
|
1722
|
+
return idx;
|
|
1671
1723
|
}
|
|
1672
|
-
|
|
1673
|
-
params,
|
|
1674
|
-
paramSymbols
|
|
1675
|
-
};
|
|
1724
|
+
for (const name of deferredAsts.keys()) compileOne(name);
|
|
1676
1725
|
}
|
|
1677
1726
|
/**
|
|
1678
|
-
*
|
|
1727
|
+
* 提取 AST 中所有 ArrowFunctionExpr 节点,编译为 FnNode,
|
|
1728
|
+
* 并将原始位置替换为 $[N] 标识符引用。
|
|
1679
1729
|
*/
|
|
1680
|
-
function
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1730
|
+
function extractAndCompileArrowFunctions(node, ctx) {
|
|
1731
|
+
switch (node.type) {
|
|
1732
|
+
case "ArrowFunctionExpr": return {
|
|
1733
|
+
type: "Identifier",
|
|
1734
|
+
name: `$[${compileArrowFunction(node, ctx)}]`
|
|
1735
|
+
};
|
|
1736
|
+
case "BinaryExpr": return {
|
|
1737
|
+
...node,
|
|
1738
|
+
left: extractAndCompileArrowFunctions(node.left, ctx),
|
|
1739
|
+
right: extractAndCompileArrowFunctions(node.right, ctx)
|
|
1740
|
+
};
|
|
1741
|
+
case "UnaryExpr": return {
|
|
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
|
+
}))
|
|
1692
1772
|
};
|
|
1773
|
+
default: return node;
|
|
1693
1774
|
}
|
|
1694
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));
|
|
1781
|
+
currentExprs(ctx).push(exprStr);
|
|
1782
|
+
return ctx.nextIndex++;
|
|
1783
|
+
}
|
|
1784
|
+
function compileShortCircuit(node, ctx) {
|
|
1785
|
+
const exprs = currentExprs(ctx);
|
|
1786
|
+
const leftIdx = compileAst(node.left, ctx);
|
|
1787
|
+
const condition = BRANCH_CONDITIONS[node.operator]?.(leftIdx);
|
|
1788
|
+
if (!condition) throw new Error(`Unexpected operator: ${node.operator}`);
|
|
1789
|
+
const branchIdx = exprs.length;
|
|
1790
|
+
exprs.push([
|
|
1791
|
+
"br",
|
|
1792
|
+
condition,
|
|
1793
|
+
0
|
|
1794
|
+
]);
|
|
1795
|
+
ctx.nextIndex++;
|
|
1796
|
+
compileAst(node.right, ctx);
|
|
1797
|
+
const skipCount = exprs.length - branchIdx - 1;
|
|
1798
|
+
exprs[branchIdx][2] = skipCount;
|
|
1799
|
+
const phiIdx = ctx.nextIndex++;
|
|
1800
|
+
exprs.push(["phi"]);
|
|
1801
|
+
return phiIdx;
|
|
1802
|
+
}
|
|
1803
|
+
function compileConditional(node, ctx) {
|
|
1804
|
+
const exprs = currentExprs(ctx);
|
|
1805
|
+
const testIdx = compileAst(node.test, ctx);
|
|
1806
|
+
const branchIdx = exprs.length;
|
|
1807
|
+
exprs.push([
|
|
1808
|
+
"br",
|
|
1809
|
+
`$[${testIdx}]`,
|
|
1810
|
+
0
|
|
1811
|
+
]);
|
|
1812
|
+
ctx.nextIndex++;
|
|
1813
|
+
compileAst(node.alternate, ctx);
|
|
1814
|
+
const jmpIdx = exprs.length;
|
|
1815
|
+
exprs.push(["jmp", 0]);
|
|
1816
|
+
ctx.nextIndex++;
|
|
1817
|
+
compileAst(node.consequent, ctx);
|
|
1818
|
+
const thenEndIdx = exprs.length;
|
|
1819
|
+
exprs[branchIdx][2] = jmpIdx - branchIdx;
|
|
1820
|
+
exprs[jmpIdx][1] = thenEndIdx - jmpIdx - 1;
|
|
1821
|
+
const phiIdx = ctx.nextIndex++;
|
|
1822
|
+
exprs.push(["phi"]);
|
|
1823
|
+
return phiIdx;
|
|
1824
|
+
}
|
|
1825
|
+
function compileArrowFunction(node, ctx) {
|
|
1826
|
+
const fnIndex = ctx.nextIndex++;
|
|
1827
|
+
const paramMapping = /* @__PURE__ */ new Map();
|
|
1828
|
+
for (const param of node.params) if (param.type === "Placeholder") paramMapping.set(param.id, `_${ctx.nextParamIndex++}`);
|
|
1829
|
+
const transformedBody = transformPlaceholders(node.body, (id) => paramMapping.get(id) ?? null);
|
|
1830
|
+
const lambdaStmts = [];
|
|
1831
|
+
ctx.expressionStack.push(lambdaStmts);
|
|
1832
|
+
compileAst(transformedBody, ctx);
|
|
1833
|
+
ctx.expressionStack.pop();
|
|
1834
|
+
const fnNode = [
|
|
1835
|
+
"fn",
|
|
1836
|
+
node.params.length,
|
|
1837
|
+
...lambdaStmts
|
|
1838
|
+
];
|
|
1839
|
+
currentExprs(ctx).push(fnNode);
|
|
1840
|
+
return fnIndex;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region src/core/evaluate.ts
|
|
1845
|
+
/**
|
|
1846
|
+
* 缓存已构造的求值函数,以提升重复执行性能
|
|
1847
|
+
*/
|
|
1848
|
+
const evaluatorCache = /* @__PURE__ */ new Map();
|
|
1695
1849
|
/**
|
|
1696
|
-
*
|
|
1697
|
-
* 使用 Placeholder 节点作为参数,在代码生成时再分配实际参数名
|
|
1850
|
+
* 判断是否是 FnNode
|
|
1698
1851
|
*/
|
|
1699
|
-
function
|
|
1700
|
-
return
|
|
1701
|
-
type: "ArrowFunctionExpr",
|
|
1702
|
-
params: Array.from({ length: paramCount }, (_, i) => ({
|
|
1703
|
-
type: "Placeholder",
|
|
1704
|
-
id: paramSymbols[i]
|
|
1705
|
-
})),
|
|
1706
|
-
body: bodyAst
|
|
1707
|
-
};
|
|
1852
|
+
function isFnNode(expr) {
|
|
1853
|
+
return Array.isArray(expr) && expr[0] === "fn" && typeof expr[1] === "number";
|
|
1708
1854
|
}
|
|
1709
1855
|
/**
|
|
1710
|
-
*
|
|
1856
|
+
* 判断是否是控制流节点
|
|
1711
1857
|
*/
|
|
1712
|
-
function
|
|
1713
|
-
|
|
1714
|
-
for (const dep of bodyDeps) if (!paramSymbols.includes(dep)) closureDeps.add(dep);
|
|
1715
|
-
return closureDeps;
|
|
1858
|
+
function isControlFlowNode(expr) {
|
|
1859
|
+
return Array.isArray(expr) && (expr[0] === "br" || expr[0] === "jmp" || expr[0] === "phi");
|
|
1716
1860
|
}
|
|
1717
|
-
|
|
1718
|
-
//#endregion
|
|
1719
|
-
//#region src/template.ts
|
|
1720
1861
|
/**
|
|
1721
|
-
*
|
|
1722
|
-
*
|
|
1723
|
-
* @example
|
|
1724
|
-
* ```ts
|
|
1725
|
-
* const name = variable<string>();
|
|
1726
|
-
* const count = variable<number>();
|
|
1727
|
-
*
|
|
1728
|
-
* const greeting = t`Hello, ${name}!`;
|
|
1729
|
-
* const message = t`You have ${count} items.`;
|
|
1862
|
+
* 生成 lambda 函数代码
|
|
1730
1863
|
*
|
|
1731
|
-
*
|
|
1732
|
-
*
|
|
1733
|
-
*
|
|
1864
|
+
* @param fnNode - FnNode 节点
|
|
1865
|
+
* @param indexCounter - 全局索引计数器
|
|
1866
|
+
* @param paramCounter - 参数计数器
|
|
1867
|
+
* @returns lambda 函数代码字符串
|
|
1734
1868
|
*/
|
|
1735
|
-
function
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1869
|
+
function generateLambdaCode(fnNode, indexCounter, paramCounter) {
|
|
1870
|
+
const [, paramCount, ...stmts] = fnNode;
|
|
1871
|
+
const paramStartIndex = paramCounter.value;
|
|
1872
|
+
paramCounter.value += paramCount;
|
|
1873
|
+
return `(${Array.from({ length: paramCount }, (_, i) => `_${paramStartIndex + i}`).join(",")})=>{${translateStmts(stmts, indexCounter, paramCounter)}}`;
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* 翻译表达式列表为 JavaScript 代码
|
|
1877
|
+
* 使用全局索引计数器分配 $[N] 槽位
|
|
1878
|
+
*/
|
|
1879
|
+
function translateStmts(expressions, indexCounter, paramCounter) {
|
|
1880
|
+
for (let i = 0; i < expressions.length; i++) {
|
|
1881
|
+
const expr = expressions[i];
|
|
1882
|
+
if (!expr) continue;
|
|
1883
|
+
if (isControlFlowNode(expr)) {
|
|
1884
|
+
const [type, , offset] = expr;
|
|
1885
|
+
if ((type === "br" || type === "jmp") && offset !== void 0) {
|
|
1886
|
+
const target = i + offset + 1;
|
|
1887
|
+
if (target < 0 || target > expressions.length) throw new Error(`Unable to translate: invalid jump target at index ${i}. Target would be ${target}, but expression length is ${expressions.length}`);
|
|
1888
|
+
}
|
|
1749
1889
|
}
|
|
1750
1890
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1891
|
+
const lines = ["let $lastValue;"];
|
|
1892
|
+
const indexMap = [];
|
|
1893
|
+
const savedStart = indexCounter.value;
|
|
1894
|
+
for (let i = 0; i < expressions.length; i++) {
|
|
1895
|
+
indexMap.push(indexCounter.value);
|
|
1896
|
+
const expr = expressions[i];
|
|
1897
|
+
if (isFnNode(expr)) {
|
|
1898
|
+
indexCounter.value++;
|
|
1899
|
+
countFnNodeIndices(expr, indexCounter);
|
|
1900
|
+
} else indexCounter.value++;
|
|
1901
|
+
}
|
|
1902
|
+
indexCounter.value = savedStart;
|
|
1903
|
+
const processed = /* @__PURE__ */ new Set();
|
|
1904
|
+
function translateRange(start, end, indent = "") {
|
|
1905
|
+
let i = start;
|
|
1906
|
+
while (i < end) {
|
|
1907
|
+
if (processed.has(i)) {
|
|
1908
|
+
i++;
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
const expr = expressions[i];
|
|
1912
|
+
if (!expr) {
|
|
1913
|
+
i++;
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
const varIdx = indexMap[i];
|
|
1917
|
+
if (isFnNode(expr)) {
|
|
1918
|
+
processed.add(i);
|
|
1919
|
+
indexCounter.value++;
|
|
1920
|
+
const lambdaCode = generateLambdaCode(expr, indexCounter, paramCounter);
|
|
1921
|
+
lines.push(`${indent}$[${varIdx}] = $lastValue = ${lambdaCode};`);
|
|
1922
|
+
i++;
|
|
1923
|
+
} else if (typeof expr === "string") {
|
|
1924
|
+
processed.add(i);
|
|
1925
|
+
indexCounter.value++;
|
|
1926
|
+
lines.push(`${indent}$[${varIdx}] = $lastValue = ${expr};`);
|
|
1927
|
+
i++;
|
|
1928
|
+
} else if (!Array.isArray(expr)) i++;
|
|
1929
|
+
else {
|
|
1930
|
+
const [type] = expr;
|
|
1931
|
+
if (type === "br") {
|
|
1932
|
+
processed.add(i);
|
|
1933
|
+
indexCounter.value++;
|
|
1934
|
+
const [, condition, skipCount] = expr;
|
|
1935
|
+
const falseBlockEnd = i + skipCount;
|
|
1936
|
+
let jmpIdx = -1;
|
|
1937
|
+
let trueBlockEnd = falseBlockEnd + 1;
|
|
1938
|
+
if (falseBlockEnd < expressions.length) {
|
|
1939
|
+
const lastInFalseBranch = expressions[falseBlockEnd];
|
|
1940
|
+
if (Array.isArray(lastInFalseBranch) && lastInFalseBranch[0] === "jmp") {
|
|
1941
|
+
jmpIdx = falseBlockEnd;
|
|
1942
|
+
const jmpOffset = lastInFalseBranch[1];
|
|
1943
|
+
trueBlockEnd = jmpIdx + jmpOffset;
|
|
1944
|
+
processed.add(jmpIdx);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
const hasElse = jmpIdx >= 0;
|
|
1948
|
+
lines.push(`${indent}if (!(${condition})) {`);
|
|
1949
|
+
translateRange(i + 1, falseBlockEnd + 1, indent + " ");
|
|
1950
|
+
if (hasElse) {
|
|
1951
|
+
lines.push(`${indent}} else {`);
|
|
1952
|
+
translateRange(jmpIdx + 1, trueBlockEnd + 1, indent + " ");
|
|
1953
|
+
lines.push(`${indent}}`);
|
|
1954
|
+
i = trueBlockEnd + 1;
|
|
1955
|
+
} else {
|
|
1956
|
+
lines.push(`${indent}}`);
|
|
1957
|
+
i = falseBlockEnd + 1;
|
|
1958
|
+
}
|
|
1959
|
+
} else if (type === "phi") {
|
|
1960
|
+
processed.add(i);
|
|
1961
|
+
indexCounter.value++;
|
|
1962
|
+
lines.push(`${indent}$[${varIdx}] = $lastValue;`);
|
|
1963
|
+
i++;
|
|
1964
|
+
} else if (type === "jmp") {
|
|
1965
|
+
indexCounter.value++;
|
|
1966
|
+
throw new Error(`Unable to translate: unexpected jmp at index ${i}. This should have been paired with a preceding br. Unsupported control flow structure.`);
|
|
1967
|
+
} else throw new Error(`Unable to translate: unknown control flow node type.`);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
translateRange(0, expressions.length);
|
|
1972
|
+
lines.push("return $lastValue;");
|
|
1973
|
+
return lines.join("\n");
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* 计算 FnNode 内部 stmts 占用的索引数量(递归)
|
|
1977
|
+
*/
|
|
1978
|
+
function countFnNodeIndices(fnNode, indexCounter) {
|
|
1979
|
+
const [, , ...stmts] = fnNode;
|
|
1980
|
+
for (const stmt of stmts) if (isFnNode(stmt)) {
|
|
1981
|
+
indexCounter.value++;
|
|
1982
|
+
countFnNodeIndices(stmt, indexCounter);
|
|
1983
|
+
} else indexCounter.value++;
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* 分析并验证控制流结构,将 br/jmp/phi 翻译为 if-else 语句
|
|
1987
|
+
*/
|
|
1988
|
+
function translateControlFlow(expressions, variableCount) {
|
|
1989
|
+
return translateStmts(expressions, { value: variableCount }, { value: 0 });
|
|
1765
1990
|
}
|
|
1766
|
-
|
|
1767
|
-
//#endregion
|
|
1768
|
-
//#region src/test-helper.ts
|
|
1769
1991
|
/**
|
|
1770
|
-
*
|
|
1771
|
-
* 自动推导变量类型和返回类型
|
|
1992
|
+
* 执行编译后的表达式
|
|
1772
1993
|
*
|
|
1773
|
-
* @template TResult -
|
|
1774
|
-
* @
|
|
1775
|
-
* @param
|
|
1776
|
-
* @
|
|
1777
|
-
*
|
|
1778
|
-
* @
|
|
1994
|
+
* @template TResult - 表达式结果类型
|
|
1995
|
+
* @param data - 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
|
|
1996
|
+
* @param values - 变量值映射,按变量名提供值
|
|
1997
|
+
* @returns 最后一个表达式的求值结果
|
|
1998
|
+
*
|
|
1999
|
+
* @throws 如果运行时类型验证失败或表达式执行出错
|
|
1779
2000
|
*
|
|
1780
2001
|
* @example
|
|
1781
2002
|
* ```ts
|
|
1782
|
-
* const
|
|
1783
|
-
* const result =
|
|
1784
|
-
*
|
|
1785
|
-
* { callback },
|
|
1786
|
-
* { callback: (fn) => fn(5) }
|
|
1787
|
-
* );
|
|
2003
|
+
* const compiled = [["x", "y"], "$[0]+$[1]", "$[1]*2"]
|
|
2004
|
+
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
|
|
2005
|
+
* // => 6 (3 * 2)
|
|
1788
2006
|
* ```
|
|
1789
2007
|
*/
|
|
1790
|
-
function
|
|
1791
|
-
|
|
2008
|
+
function evaluate(data, values) {
|
|
2009
|
+
if (data.length < 1) throw new Error("Invalid compiled data: must have at least variable names");
|
|
2010
|
+
const [variableNames, ...expressions] = data;
|
|
2011
|
+
if (!Array.isArray(variableNames)) throw new Error("Invalid compiled data: first element must be variable names array");
|
|
2012
|
+
for (const varName of variableNames) {
|
|
2013
|
+
if (typeof varName !== "string") throw new Error("Invalid compiled data: variable names must be strings");
|
|
2014
|
+
if (!(varName in values)) throw new Error(`Missing required variable: ${varName}`);
|
|
2015
|
+
}
|
|
2016
|
+
const valueArray = [];
|
|
2017
|
+
for (const varName of variableNames) valueArray.push(values[varName]);
|
|
2018
|
+
const cacheKey = JSON.stringify(data);
|
|
2019
|
+
let evaluator = evaluatorCache.get(cacheKey);
|
|
2020
|
+
if (!evaluator) {
|
|
2021
|
+
const functionBody = buildEvaluatorFunctionBody(expressions, variableNames.length);
|
|
2022
|
+
evaluator = new Function("$", functionBody);
|
|
2023
|
+
evaluatorCache.set(cacheKey, evaluator);
|
|
2024
|
+
}
|
|
2025
|
+
try {
|
|
2026
|
+
return evaluator(valueArray);
|
|
2027
|
+
} catch (error) {
|
|
2028
|
+
throw new Error(`Failed to evaluate expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
2029
|
+
}
|
|
1792
2030
|
}
|
|
1793
|
-
|
|
1794
|
-
//#endregion
|
|
1795
|
-
//#region src/wrap.ts
|
|
1796
2031
|
/**
|
|
1797
|
-
*
|
|
1798
|
-
* 返回的 Proxy 可以像 Variable 一样调用方法和访问属性
|
|
1799
|
-
*
|
|
1800
|
-
* @template T - 值的类型
|
|
1801
|
-
* @param value - 要包装的静态值(支持原始值、对象、数组、Date、RegExp 等)
|
|
1802
|
-
* @returns Proxy Expression,可以继续链式调用
|
|
1803
|
-
*
|
|
1804
|
-
* @example
|
|
1805
|
-
* ```ts
|
|
1806
|
-
* // 包装 RegExp
|
|
1807
|
-
* const pattern = wrap(/^[a-z]+$/i);
|
|
1808
|
-
* const input = variable<string>();
|
|
1809
|
-
* const result = pattern.match(input);
|
|
1810
|
-
*
|
|
1811
|
-
* // 包装 Date
|
|
1812
|
-
* const now = wrap(new Date());
|
|
1813
|
-
* const year = now.getFullYear();
|
|
2032
|
+
* 构造求值函数体
|
|
1814
2033
|
*
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1818
|
-
* ```
|
|
2034
|
+
* @param expressions - 表达式列表(可包含控制流节点和 FnNode)
|
|
2035
|
+
* @param variableCount - 变量数量
|
|
2036
|
+
* @returns 函数体字符串
|
|
1819
2037
|
*/
|
|
1820
|
-
function
|
|
1821
|
-
|
|
2038
|
+
function buildEvaluatorFunctionBody(expressions, variableCount) {
|
|
2039
|
+
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
2040
|
+
return translateControlFlow(expressions, variableCount);
|
|
1822
2041
|
}
|
|
1823
2042
|
|
|
1824
2043
|
//#endregion
|
|
1825
|
-
export { compile,
|
|
2044
|
+
export { compile, evaluate, expr, lambda, t, variable, wrap };
|
|
1826
2045
|
//# sourceMappingURL=index.mjs.map
|