@codehz/json-expr 0.5.5 → 0.6.0
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/README.md +16 -25
- package/dist/index.d.mts +57 -124
- package/dist/index.mjs +287 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -333,27 +333,6 @@ function setProxyMetadata(proxy, metadata) {
|
|
|
333
333
|
function getProxyMetadata(proxy) {
|
|
334
334
|
return proxyMetadata.get(proxy);
|
|
335
335
|
}
|
|
336
|
-
/**
|
|
337
|
-
* 检查对象是否是 Proxy variable
|
|
338
|
-
*/
|
|
339
|
-
function isProxyVariable(obj) {
|
|
340
|
-
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
341
|
-
return proxyMetadata.get(obj)?.type === "variable";
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* 检查对象是否是 Proxy expression
|
|
345
|
-
*/
|
|
346
|
-
function isProxyExpression(obj) {
|
|
347
|
-
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
348
|
-
return proxyMetadata.get(obj)?.type === "expression";
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* 检查对象是否是任意 Proxy (variable 或 expression)
|
|
352
|
-
*/
|
|
353
|
-
function isProxy(obj) {
|
|
354
|
-
if (typeof obj !== "object" && typeof obj !== "function" || obj === null) return false;
|
|
355
|
-
return proxyMetadata.has(obj);
|
|
356
|
-
}
|
|
357
336
|
|
|
358
337
|
//#endregion
|
|
359
338
|
//#region src/proxy-variable.ts
|
|
@@ -686,11 +665,10 @@ const ALLOWED_GLOBALS = new Set([
|
|
|
686
665
|
* const sum = expr({ x, y })("x + y")
|
|
687
666
|
* const result = expr({ sum, x })("sum * x")
|
|
688
667
|
* const compiled = compile(result, { x, y })
|
|
689
|
-
* // => [["x", "y"], "($0+$1)*$0"]
|
|
668
|
+
* // => [["x", "y"], "($[0]+$[1])*$[0]"]
|
|
690
669
|
* ```
|
|
691
670
|
*/
|
|
692
|
-
function compile(expression, variables,
|
|
693
|
-
const { shortCircuit = true } = options;
|
|
671
|
+
function compile(expression, variables, _options = {}) {
|
|
694
672
|
const ast = serializeArgumentToAST(expression);
|
|
695
673
|
const variableOrder = [];
|
|
696
674
|
const variableToIndex = /* @__PURE__ */ new Map();
|
|
@@ -708,13 +686,14 @@ function compile(expression, variables, options = {}) {
|
|
|
708
686
|
if (!name) return null;
|
|
709
687
|
const index = variableToIndex.get(name);
|
|
710
688
|
if (index === void 0) return null;
|
|
711
|
-
return
|
|
689
|
+
return `$[${index}]`;
|
|
712
690
|
});
|
|
713
691
|
const undefinedVars = [];
|
|
714
692
|
const transformed = transformIdentifiers(placeholderTransformed, (name) => {
|
|
715
|
-
if (name.startsWith("$") && /^\$\d
|
|
693
|
+
if (name.startsWith("$[") && /^\$\[\d+\]$/.test(name)) return name;
|
|
694
|
+
if (/^_\d+$/.test(name)) return name;
|
|
716
695
|
const index = variableToIndex.get(name);
|
|
717
|
-
if (index !== void 0) return
|
|
696
|
+
if (index !== void 0) return `$[${index}]`;
|
|
718
697
|
if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
|
|
719
698
|
return name;
|
|
720
699
|
});
|
|
@@ -722,61 +701,142 @@ function compile(expression, variables, options = {}) {
|
|
|
722
701
|
const uniqueVars = [...new Set(undefinedVars)];
|
|
723
702
|
throw new Error(`Undefined variable(s): ${uniqueVars.join(", ")}`);
|
|
724
703
|
}
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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;
|
|
780
840
|
}
|
|
781
841
|
|
|
782
842
|
//#endregion
|
|
@@ -786,10 +846,146 @@ function compile(expression, variables, options = {}) {
|
|
|
786
846
|
*/
|
|
787
847
|
const evaluatorCache = /* @__PURE__ */ new Map();
|
|
788
848
|
/**
|
|
789
|
-
*
|
|
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 函数代码字符串
|
|
790
867
|
*/
|
|
791
|
-
function
|
|
792
|
-
|
|
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
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const lines = ["let $lastValue;"];
|
|
891
|
+
const indexMap = [];
|
|
892
|
+
const savedStart = indexCounter.value;
|
|
893
|
+
for (let i = 0; i < expressions.length; i++) {
|
|
894
|
+
indexMap.push(indexCounter.value);
|
|
895
|
+
const expr = expressions[i];
|
|
896
|
+
if (isFnNode(expr)) {
|
|
897
|
+
indexCounter.value++;
|
|
898
|
+
countFnNodeIndices(expr, indexCounter);
|
|
899
|
+
} else indexCounter.value++;
|
|
900
|
+
}
|
|
901
|
+
indexCounter.value = savedStart;
|
|
902
|
+
const processed = /* @__PURE__ */ new Set();
|
|
903
|
+
function translateRange(start, end, indent = "") {
|
|
904
|
+
let i = start;
|
|
905
|
+
while (i < end) {
|
|
906
|
+
if (processed.has(i)) {
|
|
907
|
+
i++;
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
const expr = expressions[i];
|
|
911
|
+
if (!expr) {
|
|
912
|
+
i++;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
const varIdx = indexMap[i];
|
|
916
|
+
if (isFnNode(expr)) {
|
|
917
|
+
processed.add(i);
|
|
918
|
+
indexCounter.value++;
|
|
919
|
+
const lambdaCode = generateLambdaCode(expr, indexCounter, paramCounter);
|
|
920
|
+
lines.push(`${indent}$[${varIdx}] = $lastValue = ${lambdaCode};`);
|
|
921
|
+
i++;
|
|
922
|
+
} else if (typeof expr === "string") {
|
|
923
|
+
processed.add(i);
|
|
924
|
+
indexCounter.value++;
|
|
925
|
+
lines.push(`${indent}$[${varIdx}] = $lastValue = ${expr};`);
|
|
926
|
+
i++;
|
|
927
|
+
} else if (!Array.isArray(expr)) i++;
|
|
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
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
translateRange(0, expressions.length);
|
|
971
|
+
lines.push("return $lastValue;");
|
|
972
|
+
return lines.join("\n");
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* 计算 FnNode 内部 stmts 占用的索引数量(递归)
|
|
976
|
+
*/
|
|
977
|
+
function countFnNodeIndices(fnNode, indexCounter) {
|
|
978
|
+
const [, , ...stmts] = fnNode;
|
|
979
|
+
for (const stmt of stmts) if (isFnNode(stmt)) {
|
|
980
|
+
indexCounter.value++;
|
|
981
|
+
countFnNodeIndices(stmt, indexCounter);
|
|
982
|
+
} else indexCounter.value++;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* 分析并验证控制流结构,将 br/jmp/phi 翻译为 if-else 语句
|
|
986
|
+
*/
|
|
987
|
+
function translateControlFlow(expressions, variableCount) {
|
|
988
|
+
return translateStmts(expressions, { value: variableCount }, { value: 0 });
|
|
793
989
|
}
|
|
794
990
|
/**
|
|
795
991
|
* 执行编译后的表达式
|
|
@@ -803,7 +999,7 @@ function isV2Format(expressions) {
|
|
|
803
999
|
*
|
|
804
1000
|
* @example
|
|
805
1001
|
* ```ts
|
|
806
|
-
* const compiled = [["x", "y"], "$0+$1", "$1*2"]
|
|
1002
|
+
* const compiled = [["x", "y"], "$[0]+$[1]", "$[1]*2"]
|
|
807
1003
|
* const result = evaluate<number>(compiled, { x: 2, y: 3 })
|
|
808
1004
|
* // => 6 (3 * 2)
|
|
809
1005
|
* ```
|
|
@@ -821,8 +1017,8 @@ function evaluate(data, values) {
|
|
|
821
1017
|
const cacheKey = JSON.stringify(data);
|
|
822
1018
|
let evaluator = evaluatorCache.get(cacheKey);
|
|
823
1019
|
if (!evaluator) {
|
|
824
|
-
const functionBody =
|
|
825
|
-
evaluator = new Function("$
|
|
1020
|
+
const functionBody = buildEvaluatorFunctionBody(expressions, variableNames.length);
|
|
1021
|
+
evaluator = new Function("$", functionBody);
|
|
826
1022
|
evaluatorCache.set(cacheKey, evaluator);
|
|
827
1023
|
}
|
|
828
1024
|
try {
|
|
@@ -834,69 +1030,13 @@ function evaluate(data, values) {
|
|
|
834
1030
|
/**
|
|
835
1031
|
* 构造求值函数体
|
|
836
1032
|
*
|
|
837
|
-
* @param expressions -
|
|
1033
|
+
* @param expressions - 表达式列表(可包含控制流节点和 FnNode)
|
|
838
1034
|
* @param variableCount - 变量数量
|
|
839
1035
|
* @returns 函数体字符串
|
|
840
|
-
*
|
|
841
|
-
* @example
|
|
842
|
-
* ```ts
|
|
843
|
-
* buildEvaluatorFunctionBody(["$0+$1", "$2*2"], 2)
|
|
844
|
-
* // 返回执行 $0+$1 并存储到 $values[2],然后执行 $2*2 的函数体
|
|
845
|
-
* ```
|
|
846
1036
|
*/
|
|
847
1037
|
function buildEvaluatorFunctionBody(expressions, variableCount) {
|
|
848
1038
|
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
849
|
-
return
|
|
850
|
-
...Array.from({ length: variableCount }, (_, i) => `const $${i} = $values[${i}];`),
|
|
851
|
-
...expressions.map((expr, i) => {
|
|
852
|
-
const idx = variableCount + i;
|
|
853
|
-
return `const $${idx} = ${expr}; $values[${idx}] = $${idx};`;
|
|
854
|
-
}),
|
|
855
|
-
`return $values[$values.length - 1];`
|
|
856
|
-
].join("\n");
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* 构造带控制流支持的求值函数体(V2 格式)
|
|
860
|
-
*
|
|
861
|
-
* @param expressions - 表达式列表(可包含控制流节点)
|
|
862
|
-
* @param variableCount - 变量数量
|
|
863
|
-
* @returns 函数体字符串
|
|
864
|
-
*/
|
|
865
|
-
function buildEvaluatorFunctionBodyV2(expressions, variableCount) {
|
|
866
|
-
if (expressions.length === 0) throw new Error("No expressions to evaluate");
|
|
867
|
-
const lines = [
|
|
868
|
-
...Array.from({ length: variableCount }, (_, i) => `const $${i} = $values[${i}];`),
|
|
869
|
-
"let $pc = 0;",
|
|
870
|
-
"let $lastValue;",
|
|
871
|
-
...expressions.map((_, i) => `let $${variableCount + i};`),
|
|
872
|
-
`while ($pc < ${expressions.length}) {`,
|
|
873
|
-
" switch ($pc) {"
|
|
874
|
-
];
|
|
875
|
-
expressions.forEach((expr, i) => {
|
|
876
|
-
const idx = variableCount + i;
|
|
877
|
-
lines.push(` case ${i}: {`);
|
|
878
|
-
if (typeof expr === "string") {
|
|
879
|
-
lines.push(` $${idx} = $lastValue = ${expr};`);
|
|
880
|
-
lines.push(` $values[${idx}] = $${idx};`);
|
|
881
|
-
lines.push(" $pc++; break;");
|
|
882
|
-
} else {
|
|
883
|
-
const [type] = expr;
|
|
884
|
-
switch (type) {
|
|
885
|
-
case "br":
|
|
886
|
-
lines.push(` if (${expr[1]}) { $pc += ${expr[2] + 1}; } else { $pc++; } break;`);
|
|
887
|
-
break;
|
|
888
|
-
case "jmp":
|
|
889
|
-
lines.push(` $pc += ${expr[1] + 1}; break;`);
|
|
890
|
-
break;
|
|
891
|
-
case "phi":
|
|
892
|
-
lines.push(` $${idx} = $values[${idx}] = $lastValue; $pc++; break;`);
|
|
893
|
-
break;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
lines.push(" }");
|
|
897
|
-
});
|
|
898
|
-
lines.push(" }", "}", "return $values[$values.length - 1];");
|
|
899
|
-
return lines.join("\n");
|
|
1039
|
+
return translateControlFlow(expressions, variableCount);
|
|
900
1040
|
}
|
|
901
1041
|
|
|
902
1042
|
//#endregion
|
|
@@ -1682,5 +1822,5 @@ function wrap(value) {
|
|
|
1682
1822
|
}
|
|
1683
1823
|
|
|
1684
1824
|
//#endregion
|
|
1685
|
-
export { compile, compileAndEvaluate, evaluate, expr,
|
|
1825
|
+
export { compile, compileAndEvaluate, evaluate, expr, lambda, t, variable, wrap };
|
|
1686
1826
|
//# sourceMappingURL=index.mjs.map
|