Skip to content

Commit 775a906

Browse files
committed
refactor: change variable reference format from $0 to $[0] and optimize evaluator
- Change variable reference format from $0, $1 to $[0], $[1] in compiled expressions - Optimize evaluate.ts by removing intermediate variable declarations - Update all tests and documentation to use new format - Simplify code generation by directly using $ array for all operations
1 parent 4a87bf9 commit 775a906

7 files changed

Lines changed: 52 additions & 63 deletions

File tree

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const result = expr({ sum, product })("sum + product");
4141

4242
// 编译表达式(可序列化为 JSON)
4343
const compiled = compile(result, { x, y });
44-
// => [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
44+
// => [["x", "y"], "$[0]+$[1]", "$[0]*$[1]", "$[2]+$[3]"]
4545

4646
// 执行编译后的表达式
4747
const value = evaluate(compiled, { x: 2, y: 3 });
@@ -102,7 +102,7 @@ const array = expr({ x, y })("[x, y].filter(v => v > 0)");
102102
// 其中 $N 用于引用之前的变量或表达式
103103

104104
const compiled = compile(result, { x, y });
105-
// [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
105+
// [["x", "y"], "$[0]+$[1]", "$[0]*$[1]", "$[2]+$[3]"]
106106
```
107107

108108
## API 参考
@@ -169,11 +169,11 @@ const product = expr({ x, y })("x * y");
169169
const result = expr({ sum, product })("sum + product");
170170

171171
const compiled = compile(result, { x, y });
172-
// [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
172+
// [["x", "y"], "$[0]+$[1]", "$[0]*$[1]", "$[2]+$[3]"]
173173

174174
// 禁用内联优化
175175
const noInline = compile(result, { x, y }, { inline: false });
176-
// [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
176+
// [["x", "y"], "$[0]+$[1]", "$[0]*$[1]", "$[2]+$[3]"]
177177
```
178178

179179
### `evaluate<TResult>(data: CompiledData, values: Record<string, unknown>): TResult`
@@ -600,7 +600,7 @@ const compiled = compile(orExpr, { a, b });
600600

601601
// 当 a 为 true 时,b 不会被求值
602602
// 编译数据包含控制流节点:
603-
// [["a", "b"], ["br", "$0", 1], "$1", ["phi"]]
603+
// [["a", "b"], ["br", "$[0]", 1], "$[1]", ["phi"]]
604604

605605
// 空值合并
606606
const x = variable<number | null>();
@@ -627,8 +627,8 @@ const product = expr({ x, y })("x * y");
627627
const result = expr({ sum, product })("sum + product");
628628

629629
// 自动内联后,编译结果为:
630-
// [["x", "y"], "($0+$1)+($0*$1)"]
631-
// 而不是 [["x", "y"], "$0+$1", "$0*$1", "$2+$3"]
630+
// [["x", "y"], "($[0]+$[1])+($[0]*$[1])"]
631+
// 而不是 [["x", "y"], "$[0]+$[1]", "$[0]*$[1]", "$[2]+$[3]"]
632632

633633
const compiled = compile(result, { x, y });
634634
const value = evaluate(compiled, { x: 2, y: 3 });
@@ -665,7 +665,7 @@ const compiled = compile(result, { x, y });
665665

666666
// 序列化
667667
const json = JSON.stringify(compiled);
668-
// "[["x","y"],"$0+$1","$0*$1","$2+$3"]"
668+
// "[["x","y"],"$[0]+$[1]","$[0]*$[1]","$[2]+$[3]"]"
669669

670670
// 存储或传输...
671671

@@ -688,8 +688,8 @@ const sum = expr({ x, y })("x + y");
688688
const compiled = compile(sum, { x, y });
689689

690690
// 输出
691-
// [["x", "y"], "$0+$1"]
692-
// $0 引用 x,$1 引用 y
691+
// [["x", "y"], "$[0]+$[1]"]
692+
// $[0] 引用 x,$[1] 引用 y
693693
```
694694

695695
### V2 格式(控制流节点)
@@ -704,8 +704,8 @@ const compiled = compile(result, { a, b });
704704
// 输出
705705
// [
706706
// ["a", "b"],
707-
// ["br", "$0", 1], // 如果 $0 为 truthy,跳过 1 条指令
708-
// "$1", // 否则求值 $1
707+
// ["br", "$[0]", 1], // 如果 $[0] 为 truthy,跳过 1 条指令
708+
// "$[1]", // 否则求值 $[1]
709709
// ["phi"] // 取最近求值结果
710710
// ]
711711
```
@@ -736,7 +736,7 @@ const xy = variable<number>();
736736
const conflict = expr({ xy, x })("xy + x");
737737
// 正确处理:编译器能区分 xy 和 x
738738
const compiled = compile(conflict, { xy, x });
739-
// => [["xy", "x"], "$0+$1"]
739+
// => [["xy", "x"], "$[0]+$[1]"]
740740
```
741741

742742
### 运行时错误

src/compile.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ describe("compile 单元测试", () => {
2222
const sum = expr({ x, y })("x + y");
2323
const result = compile(sum, { x, y });
2424

25-
// 变量被替换为 $0, $1
26-
expect(result[1]).toBe("$0+$1");
25+
// 变量被替换为 $[0], $[1]
26+
expect(result[1]).toBe("$[0]+$[1]");
2727
});
2828

2929
test("变量顺序与声明顺序一致", () => {
@@ -46,7 +46,7 @@ describe("compile 单元测试", () => {
4646
const result = compile(e, { xy, x });
4747

4848
expect(result[0]).toEqual(["xy", "x"]);
49-
expect(result[1]).toBe("$0+$1");
49+
expect(result[1]).toBe("$[0]+$[1]");
5050
});
5151
});
5252

@@ -90,7 +90,7 @@ describe("compile 单元测试", () => {
9090
const result = compile(e, { x, y });
9191

9292
// 输出应该是规范化的
93-
expect(result[1]).toBe("$0+$1*2");
93+
expect(result[1]).toBe("$[0]+$[1]*2");
9494
});
9595

9696
test("保留必要的括号", () => {
@@ -101,7 +101,7 @@ describe("compile 单元测试", () => {
101101
const result = compile(e, { x, y });
102102

103103
// 括号应该被保留以保持正确的运算顺序
104-
expect(result[1]).toBe("($0+$1)*2");
104+
expect(result[1]).toBe("($[0]+$[1])*2");
105105
});
106106
});
107107

@@ -114,23 +114,23 @@ describe("compile 单元测试", () => {
114114
const result = compile({ sum, x, constant: 1 }, { x, y });
115115

116116
expect(result[0]).toEqual(["x", "y"]);
117-
expect(result[1]).toBe("{sum:$0+$1,x:$0,constant:1}");
117+
expect(result[1]).toBe("{sum:$[0]+$[1],x:$[0],constant:1}");
118118
});
119119

120120
test("支持数组中包含 Proxy", () => {
121121
const x = variable<number[]>();
122122
const result = compile([x, x.at(0), 42], { x });
123123

124124
expect(result[0]).toEqual(["x"]);
125-
expect(result[1]).toBe("[$0,$0.at(0),42]");
125+
expect(result[1]).toBe("[$[0],$[0].at(0),42]");
126126
});
127127

128128
test("支持直接传入 root variable", () => {
129129
const x = variable<number>();
130130
const result = compile(x, { x });
131131

132132
expect(result[0]).toEqual(["x"]);
133-
expect(result[1]).toBe("$0");
133+
expect(result[1]).toBe("$[0]");
134134
});
135135

136136
test("支持原始值", () => {

src/compile.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export interface CompileOptions {}
6464
* const sum = expr({ x, y })("x + y")
6565
* const result = expr({ sum, x })("sum * x")
6666
* const compiled = compile(result, { x, y })
67-
* // => [["x", "y"], "($0+$1)*$0"]
67+
* // => [["x", "y"], "($[0]+$[1])*$[0]"]
6868
* ```
6969
*/
7070
export function compile<TResult>(
@@ -90,24 +90,24 @@ export function compile<TResult>(
9090
}
9191
}
9292

93-
// 第一步:转换 Placeholder 节点为 $N 格式的 Identifier
93+
// 第一步:转换 Placeholder 节点为 $[N] 格式的 Identifier
9494
// lambda 参数的 Placeholder 保留不转换(返回 null)
9595
const placeholderTransformed = transformPlaceholders(ast, (id) => {
9696
const name = symbolToName.get(id);
9797
if (!name) return null; // 不是变量占位符(可能是 lambda 参数),保留
9898
const index = variableToIndex.get(name);
9999
if (index === undefined) return null;
100-
return `$${index}`;
100+
return `$[${index}]`;
101101
});
102102

103103
// 第二步:检查是否有未定义的 Identifier(非全局对象)
104104
const undefinedVars: string[] = [];
105105
const transformed = transformIdentifiers(placeholderTransformed, (name) => {
106-
// 已经转换为 $N 的跳过
107-
if (name.startsWith("$") && /^\$\d+$/.test(name)) return name;
106+
// 已经转换为 $[N] 的跳过
107+
if (name.startsWith("$[") && /^\$\[\d+\]$/.test(name)) return name;
108108

109109
const index = variableToIndex.get(name);
110-
if (index !== undefined) return `$${index}`;
110+
if (index !== undefined) return `$[${index}]`;
111111

112112
if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
113113
return name;
@@ -138,9 +138,9 @@ export function compile<TResult>(
138138
const leftIdx = compileAst(node.left);
139139

140140
const branchConditions: Record<string, string> = {
141-
"||": `$${leftIdx}`,
142-
"&&": `!$${leftIdx}`,
143-
"??": `$${leftIdx}!=null`,
141+
"||": `$[${leftIdx}]`,
142+
"&&": `!$[${leftIdx}]`,
143+
"??": `$[${leftIdx}]!=null`,
144144
};
145145

146146
const branchIdx = expressions.length;
@@ -161,7 +161,7 @@ export function compile<TResult>(
161161
const testIdx = compileAst(node.test);
162162

163163
const branchIdx = expressions.length;
164-
expressions.push(["br", `$${testIdx}`, 0] as BranchNode);
164+
expressions.push(["br", `$[${testIdx}]`, 0] as BranchNode);
165165
nextIndex++;
166166

167167
compileAst(node.alternate);

src/evaluate.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,7 @@ function translateControlFlow(expressions: CompiledExpression[], variableCount:
3737
}
3838
}
3939

40-
const lines: string[] = [
41-
// 初始化变量
42-
...Array.from({ length: variableCount }, (_, i) => `const $${i} = $values[${i}];`),
43-
"let $lastValue;",
44-
];
45-
46-
// 预先声明所有中间变量
47-
for (let i = 0; i < expressions.length; i++) {
48-
lines.push(`let $${variableCount + i};`);
49-
}
40+
const lines: string[] = ["let $lastValue;"];
5041

5142
// 追踪已处理过的指令索引
5243
const processed = new Set<number>();
@@ -66,8 +57,7 @@ function translateControlFlow(expressions: CompiledExpression[], variableCount:
6657
if (typeof expr === "string") {
6758
// 普通表达式
6859
processed.add(i);
69-
lines.push(`${indent}$${varIdx} = $lastValue = ${expr};`);
70-
lines.push(`${indent}$values[${varIdx}] = $${varIdx};`);
60+
lines.push(`${indent}$[${varIdx}] = $lastValue = ${expr};`);
7161
i++;
7262
} else if (!Array.isArray(expr)) {
7363
i++;
@@ -119,8 +109,7 @@ function translateControlFlow(expressions: CompiledExpression[], variableCount:
119109
} else if (type === "phi") {
120110
// Phi 节点:取最近的值
121111
processed.add(i);
122-
lines.push(`${indent}$${varIdx} = $lastValue;`);
123-
lines.push(`${indent}$values[${varIdx}] = $${varIdx};`);
112+
lines.push(`${indent}$[${varIdx}] = $lastValue;`);
124113
i++;
125114
} else if (type === "jmp") {
126115
// jmp 不应该单独出现,应该已被 br 处理
@@ -154,7 +143,7 @@ function translateControlFlow(expressions: CompiledExpression[], variableCount:
154143
*
155144
* @example
156145
* ```ts
157-
* const compiled = [["x", "y"], "$0+$1", "$1*2"]
146+
* const compiled = [["x", "y"], "$[0]+$[1]", "$[1]*2"]
158147
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
159148
* // => 6 (3 * 2)
160149
* ```
@@ -194,7 +183,7 @@ export function evaluate<TResult = unknown>(data: CompiledData, values: Record<s
194183
// 默认使用 V2 格式的函数体构造器
195184
const functionBody = buildEvaluatorFunctionBodyV2(expressions, variableNames.length);
196185
// eslint-disable-next-line @typescript-eslint/no-implied-eval
197-
evaluator = new Function("$values", functionBody) as (values: unknown[]) => unknown;
186+
evaluator = new Function("$", functionBody) as (values: unknown[]) => unknown;
198187
evaluatorCache.set(cacheKey, evaluator);
199188
}
200189

src/integration.short-circuit.test.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,12 @@ describe("短路求值测试", () => {
406406
const result = expr({ a, b })("a || b");
407407
const compiled = compile(result, { a, b });
408408

409-
// 应该生成: $0, ["br", "$2", 1], $1, ["phi"]
410-
// 偏移量为 1,因为只需跳过 $1 一条指令
409+
// 应该生成: $[0], ["br", "$[2]", 1], $[1], ["phi"]
410+
// 偏移量为 1,因为只需跳过 $[1] 一条指令
411411
expect(compiled[0]).toEqual(["a", "b"]);
412-
expect(compiled[1]).toBe("$0"); // 左操作数
413-
expect(compiled[2]).toEqual(["br", "$2", 1]); // 如果 $2 为 true,跳过 1 条
414-
expect(compiled[3]).toBe("$1"); // 右操作数
412+
expect(compiled[1]).toBe("$[0]"); // 左操作数
413+
expect(compiled[2]).toEqual(["br", "$[2]", 1]); // 如果 $[2] 为 true,跳过 1 条
414+
expect(compiled[3]).toBe("$[1]"); // 右操作数
415415
expect(compiled[4]).toEqual(["phi"]); // phi 节点
416416
});
417417

@@ -423,9 +423,9 @@ describe("短路求值测试", () => {
423423
const compiled = compile(result, { a, b });
424424

425425
expect(compiled[0]).toEqual(["a", "b"]);
426-
expect(compiled[1]).toBe("$0");
427-
expect(compiled[2]).toEqual(["br", "!$2", 1]); // 如果 !$2 为 true,跳过 1 条
428-
expect(compiled[3]).toBe("$1");
426+
expect(compiled[1]).toBe("$[0]");
427+
expect(compiled[2]).toEqual(["br", "!$[2]", 1]); // 如果 !$[2] 为 true,跳过 1 条
428+
expect(compiled[3]).toBe("$[1]");
429429
expect(compiled[4]).toEqual(["phi"]);
430430
});
431431

@@ -437,9 +437,9 @@ describe("短路求值测试", () => {
437437
const compiled = compile(result, { a, b });
438438

439439
expect(compiled[0]).toEqual(["a", "b"]);
440-
expect(compiled[1]).toBe("$0");
441-
expect(compiled[2]).toEqual(["br", "$2!=null", 1]); // 如果非 null,跳过 1 条
442-
expect(compiled[3]).toBe("$1");
440+
expect(compiled[1]).toBe("$[0]");
441+
expect(compiled[2]).toEqual(["br", "$[2]!=null", 1]); // 如果非 null,跳过 1 条
442+
expect(compiled[3]).toBe("$[1]");
443443
expect(compiled[4]).toEqual(["phi"]);
444444
});
445445

@@ -451,9 +451,9 @@ describe("短路求值测试", () => {
451451
const result = expr({ cond, x, y })("cond ? x : y");
452452
const compiled = compile(result, { cond, x, y });
453453

454-
// 结构: $0 (cond), ["br", "$3", offset], $2 (else), ["jmp", offset], $1 (then), ["phi"]
454+
// 结构: $[0] (cond), ["br", "$[3]", offset], $[2] (else), ["jmp", offset], $[1] (then), ["phi"]
455455
expect(compiled[0]).toEqual(["cond", "x", "y"]);
456-
expect(compiled[1]).toBe("$0"); // 条件
456+
expect(compiled[1]).toBe("$[0]"); // 条件
457457
expect(compiled[2]![0]).toBe("br"); // 条件跳转
458458
expect(compiled[4]![0]).toBe("jmp"); // 无条件跳转
459459
expect(compiled[compiled.length - 1]).toEqual(["phi"]); // phi 节点

src/lambda.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ describe("嵌套 lambda", () => {
671671
);
672672

673673
const compiled = compile(processed, { matrix });
674-
// 编译结果: $0.map((_0,_1)=>_0.map(_2=>_2+_1*100))
674+
// 编译结果: $[0].map((_0,_1)=>_0.map(_2=>_2+_1*100))
675675
expect(compiled[1]).toContain("(_0,_1)=>_0.map(_2=>");
676676

677677
const result = evaluate(compiled, {

src/parser.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ describe("parser 单元测试", () => {
185185
test("使用映射", () => {
186186
const ast = parse("a + b * 2");
187187
const mapping: Record<string, number> = { a: 0, b: 1 };
188-
const transformed = transformIdentifiers(ast, (name) => (name in mapping ? `$${mapping[name]}` : name));
189-
expect(generate(transformed)).toBe("$0+$1*2");
188+
const transformed = transformIdentifiers(ast, (name) => (name in mapping ? `$[${mapping[name]}]` : name));
189+
expect(generate(transformed)).toBe("$[0]+$[1]*2");
190190
});
191191

192192
test("成员访问保留属性名", () => {

0 commit comments

Comments
 (0)