@codehz/json-expr 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -194,6 +194,8 @@ var Parser = class Parser {
194
194
  if (ch === "[") return this.parseArray();
195
195
  if (ch === "{") return this.parseObject();
196
196
  if (ch === "(") {
197
+ const arrowFunc = this.tryParseArrowFunction();
198
+ if (arrowFunc) return arrowFunc;
197
199
  this.advance();
198
200
  this.skipWhitespace();
199
201
  const expr = this.parseExpression();
@@ -214,7 +216,11 @@ var Parser = class Parser {
214
216
  type: "Identifier",
215
217
  name: "undefined"
216
218
  };
217
- if (this.isIdentifierStart(ch)) return this.parseIdentifier();
219
+ if (this.isIdentifierStart(ch)) {
220
+ const arrowFunc = this.tryParseSingleParamArrowFunction();
221
+ if (arrowFunc) return arrowFunc;
222
+ return this.parseIdentifier();
223
+ }
218
224
  throw new Error(`Unexpected character at position ${this.pos}: ${ch}`);
219
225
  }
220
226
  parseNumber() {
@@ -359,6 +365,63 @@ var Parser = class Parser {
359
365
  name
360
366
  };
361
367
  }
368
+ /**
369
+ * 尝试解析带括号的箭头函数: (a, b) => expr
370
+ * 使用回溯机制
371
+ */
372
+ tryParseArrowFunction() {
373
+ const savedPos = this.pos;
374
+ try {
375
+ this.expect("(");
376
+ this.skipWhitespace();
377
+ const params = [];
378
+ while (this.peek() !== ")") {
379
+ if (!this.isIdentifierStart(this.peek())) throw new Error("Expected identifier");
380
+ params.push(this.parseIdentifier());
381
+ this.skipWhitespace();
382
+ if (this.peek() === ",") {
383
+ this.advance();
384
+ this.skipWhitespace();
385
+ } else break;
386
+ }
387
+ this.expect(")");
388
+ this.skipWhitespace();
389
+ if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
390
+ this.pos += 2;
391
+ this.skipWhitespace();
392
+ return {
393
+ type: "ArrowFunctionExpr",
394
+ params,
395
+ body: this.parseExpression()
396
+ };
397
+ } catch {
398
+ this.pos = savedPos;
399
+ return null;
400
+ }
401
+ }
402
+ /**
403
+ * 尝试解析单参数无括号的箭头函数: a => expr
404
+ * 使用回溯机制
405
+ */
406
+ tryParseSingleParamArrowFunction() {
407
+ const savedPos = this.pos;
408
+ try {
409
+ const param = this.parseIdentifier();
410
+ this.skipWhitespace();
411
+ if (this.source.slice(this.pos, this.pos + 2) !== "=>") throw new Error("Expected =>");
412
+ this.pos += 2;
413
+ this.skipWhitespace();
414
+ const body = this.parseExpression();
415
+ return {
416
+ type: "ArrowFunctionExpr",
417
+ params: [param],
418
+ body
419
+ };
420
+ } catch {
421
+ this.pos = savedPos;
422
+ return null;
423
+ }
424
+ }
362
425
  parseArguments() {
363
426
  const args = [];
364
427
  this.skipWhitespace();
@@ -496,6 +559,27 @@ function generate(node) {
496
559
  case "CallExpr": {
497
560
  const callee = wrapIfNeeded(node.callee, node, "callee");
498
561
  const args = node.arguments.map(generate).join(",");
562
+ if (node.callee.type === "Identifier" && [
563
+ "Date",
564
+ "RegExp",
565
+ "URL",
566
+ "URLSearchParams",
567
+ "Map",
568
+ "Set",
569
+ "Int8Array",
570
+ "Uint8Array",
571
+ "Uint8ClampedArray",
572
+ "Int16Array",
573
+ "Uint16Array",
574
+ "Int32Array",
575
+ "Uint32Array",
576
+ "Float32Array",
577
+ "Float64Array",
578
+ "BigInt64Array",
579
+ "BigUint64Array",
580
+ "ArrayBuffer",
581
+ "DataView"
582
+ ].includes(node.callee.name)) return node.optional ? `new ${callee}?.(${args})` : `new ${callee}(${args})`;
499
583
  return node.optional ? `${callee}?.(${args})` : `${callee}(${args})`;
500
584
  }
501
585
  case "ArrayExpr": return `[${node.elements.map(generate).join(",")}]`;
@@ -503,6 +587,13 @@ function generate(node) {
503
587
  if (prop.shorthand) return generate(prop.key);
504
588
  return `${prop.computed ? `[${generate(prop.key)}]` : generate(prop.key)}:${generate(prop.value)}`;
505
589
  }).join(",")}}`;
590
+ case "ArrowFunctionExpr": {
591
+ const params = node.params.map((p) => p.name).join(",");
592
+ let body = generate(node.body);
593
+ if (node.body.type === "ObjectExpr") body = `(${body})`;
594
+ if (node.params.length === 1) return `${params}=>${body}`;
595
+ return `(${params})=>${body}`;
596
+ }
506
597
  default: {
507
598
  const nodeType = node.type ?? "unknown";
508
599
  throw new Error(`Unknown node type: ${nodeType}`);
@@ -537,6 +628,68 @@ function needsParens(child, parent, position) {
537
628
  }
538
629
  return false;
539
630
  }
631
+ /**
632
+ * 转换 AST 中的标识符
633
+ * 回调函数可以返回:
634
+ * - string: 替换标识符名称
635
+ * - ASTNode: 内联该 AST 节点(用于子表达式内联)
636
+ */
637
+ function transformIdentifiers(node, transform) {
638
+ switch (node.type) {
639
+ case "Identifier": {
640
+ const result = transform(node.name);
641
+ return typeof result === "string" ? {
642
+ ...node,
643
+ name: result
644
+ } : result;
645
+ }
646
+ case "BinaryExpr": return {
647
+ ...node,
648
+ left: transformIdentifiers(node.left, transform),
649
+ right: transformIdentifiers(node.right, transform)
650
+ };
651
+ case "UnaryExpr": return {
652
+ ...node,
653
+ argument: transformIdentifiers(node.argument, transform)
654
+ };
655
+ case "ConditionalExpr": return {
656
+ ...node,
657
+ test: transformIdentifiers(node.test, transform),
658
+ consequent: transformIdentifiers(node.consequent, transform),
659
+ alternate: transformIdentifiers(node.alternate, transform)
660
+ };
661
+ case "MemberExpr": return {
662
+ ...node,
663
+ object: transformIdentifiers(node.object, transform),
664
+ property: node.computed ? transformIdentifiers(node.property, transform) : node.property
665
+ };
666
+ case "CallExpr": return {
667
+ ...node,
668
+ callee: transformIdentifiers(node.callee, transform),
669
+ arguments: node.arguments.map((arg) => transformIdentifiers(arg, transform))
670
+ };
671
+ case "ArrayExpr": return {
672
+ ...node,
673
+ elements: node.elements.map((el) => transformIdentifiers(el, transform))
674
+ };
675
+ case "ObjectExpr": return {
676
+ ...node,
677
+ properties: node.properties.map((prop) => ({
678
+ ...prop,
679
+ key: prop.computed ? transformIdentifiers(prop.key, transform) : prop.key,
680
+ value: transformIdentifiers(prop.value, transform)
681
+ }))
682
+ };
683
+ case "ArrowFunctionExpr": {
684
+ const paramNames = new Set(node.params.map((p) => p.name));
685
+ return {
686
+ ...node,
687
+ body: transformIdentifiers(node.body, (name) => paramNames.has(name) ? name : transform(name))
688
+ };
689
+ }
690
+ default: return node;
691
+ }
692
+ }
540
693
 
541
694
  //#endregion
542
695
  //#region src/proxy-metadata.ts
@@ -588,25 +741,258 @@ function getVariablePlaceholder$1(id) {
588
741
  return `$$VAR_${id.description}$$`;
589
742
  }
590
743
  /**
591
- * 序列化参数为表达式字符串
592
- * - Proxy Variable/Expression:使用源码或占位符
593
- * - 数组:递归处理元素
594
- * - 对象:递归处理属性
595
- * - 原始值:JSON.stringify
744
+ * 序列化参数为 AST 节点
745
+ * - Proxy Variable/Expression:使用 ast 或占位符标识符
746
+ * - 数组:返回 ArrayExpr 节点
747
+ * - 对象:返回 ObjectExpr 节点
748
+ * - 原始值:返回对应的字面量节点
749
+ * - Date, RegExp, BigInt, URL, URLSearchParams, Map, Set, TypedArray, DataView: 构造函数调用
596
750
  */
597
- function serializeArgument(arg) {
751
+ function serializeArgumentToAST(arg) {
598
752
  if ((typeof arg === "object" || typeof arg === "function") && arg !== null) {
599
753
  const meta = getProxyMetadata(arg);
600
754
  if (meta) {
601
- if (meta.source) return meta.source;
602
- if (meta.rootVariable) return getVariablePlaceholder$1(meta.rootVariable);
755
+ if (meta.ast) return meta.ast;
756
+ if (meta.rootVariable) return {
757
+ type: "Identifier",
758
+ name: getVariablePlaceholder$1(meta.rootVariable)
759
+ };
603
760
  }
604
761
  }
605
- if (Array.isArray(arg)) return `[${arg.map(serializeArgument).join(", ")}]`;
606
- if (typeof arg === "object" && arg !== null) return `{${Object.entries(arg).map(([k, v]) => {
607
- return `${/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k)}: ${serializeArgument(v)}`;
608
- }).join(", ")}}`;
609
- return JSON.stringify(arg);
762
+ if (Array.isArray(arg)) return {
763
+ type: "ArrayExpr",
764
+ elements: arg.map(serializeArgumentToAST)
765
+ };
766
+ if (typeof arg === "object" && arg !== null) {
767
+ if (arg instanceof Date) return {
768
+ type: "CallExpr",
769
+ callee: {
770
+ type: "Identifier",
771
+ name: "Date"
772
+ },
773
+ arguments: [{
774
+ type: "NumberLiteral",
775
+ value: arg.getTime(),
776
+ raw: String(arg.getTime())
777
+ }],
778
+ optional: false
779
+ };
780
+ if (arg instanceof RegExp) {
781
+ const args = [{
782
+ type: "StringLiteral",
783
+ value: arg.source,
784
+ quote: "\""
785
+ }];
786
+ if (arg.flags) args.push({
787
+ type: "StringLiteral",
788
+ value: arg.flags,
789
+ quote: "\""
790
+ });
791
+ return {
792
+ type: "CallExpr",
793
+ callee: {
794
+ type: "Identifier",
795
+ name: "RegExp"
796
+ },
797
+ arguments: args,
798
+ optional: false
799
+ };
800
+ }
801
+ if (typeof URL !== "undefined" && arg instanceof URL) return {
802
+ type: "CallExpr",
803
+ callee: {
804
+ type: "Identifier",
805
+ name: "URL"
806
+ },
807
+ arguments: [{
808
+ type: "StringLiteral",
809
+ value: arg.href,
810
+ quote: "\""
811
+ }],
812
+ optional: false
813
+ };
814
+ if (typeof URLSearchParams !== "undefined" && arg instanceof URLSearchParams) {
815
+ const entries = [];
816
+ arg.forEach((value, key) => {
817
+ entries.push({
818
+ type: "ArrayExpr",
819
+ elements: [{
820
+ type: "StringLiteral",
821
+ value: key,
822
+ quote: "\""
823
+ }, {
824
+ type: "StringLiteral",
825
+ value,
826
+ quote: "\""
827
+ }]
828
+ });
829
+ });
830
+ return {
831
+ type: "CallExpr",
832
+ callee: {
833
+ type: "Identifier",
834
+ name: "URLSearchParams"
835
+ },
836
+ arguments: [{
837
+ type: "ArrayExpr",
838
+ elements: entries
839
+ }],
840
+ optional: false
841
+ };
842
+ }
843
+ if (arg instanceof Map) {
844
+ const entries = [];
845
+ arg.forEach((value, key) => {
846
+ entries.push({
847
+ type: "ArrayExpr",
848
+ elements: [serializeArgumentToAST(key), serializeArgumentToAST(value)]
849
+ });
850
+ });
851
+ return {
852
+ type: "CallExpr",
853
+ callee: {
854
+ type: "Identifier",
855
+ name: "Map"
856
+ },
857
+ arguments: [{
858
+ type: "ArrayExpr",
859
+ elements: entries
860
+ }],
861
+ optional: false
862
+ };
863
+ }
864
+ if (arg instanceof Set) {
865
+ const values = [];
866
+ arg.forEach((value) => {
867
+ values.push(serializeArgumentToAST(value));
868
+ });
869
+ return {
870
+ type: "CallExpr",
871
+ callee: {
872
+ type: "Identifier",
873
+ name: "Set"
874
+ },
875
+ arguments: [{
876
+ type: "ArrayExpr",
877
+ elements: values
878
+ }],
879
+ optional: false
880
+ };
881
+ }
882
+ for (const constructorName of [
883
+ "Int8Array",
884
+ "Uint8Array",
885
+ "Uint8ClampedArray",
886
+ "Int16Array",
887
+ "Uint16Array",
888
+ "Int32Array",
889
+ "Uint32Array",
890
+ "Float32Array",
891
+ "Float64Array",
892
+ "BigInt64Array",
893
+ "BigUint64Array"
894
+ ]) if (typeof globalThis[constructorName] !== "undefined") {
895
+ if (arg instanceof globalThis[constructorName]) {
896
+ const values = [...arg].map((val) => serializeArgumentToAST(val));
897
+ return {
898
+ type: "CallExpr",
899
+ callee: {
900
+ type: "Identifier",
901
+ name: constructorName
902
+ },
903
+ arguments: [{
904
+ type: "ArrayExpr",
905
+ elements: values
906
+ }],
907
+ optional: false
908
+ };
909
+ }
910
+ }
911
+ if (arg instanceof ArrayBuffer) {
912
+ const uint8Array = new Uint8Array(arg);
913
+ return {
914
+ type: "MemberExpr",
915
+ object: {
916
+ type: "CallExpr",
917
+ callee: {
918
+ type: "Identifier",
919
+ name: "Uint8Array"
920
+ },
921
+ arguments: [{
922
+ type: "ArrayExpr",
923
+ elements: Array.from(uint8Array).map((val) => serializeArgumentToAST(val))
924
+ }],
925
+ optional: false
926
+ },
927
+ property: {
928
+ type: "Identifier",
929
+ name: "buffer"
930
+ },
931
+ computed: false,
932
+ optional: false
933
+ };
934
+ }
935
+ if (arg instanceof DataView) return {
936
+ type: "CallExpr",
937
+ callee: {
938
+ type: "Identifier",
939
+ name: "DataView"
940
+ },
941
+ arguments: [serializeArgumentToAST(arg.buffer)],
942
+ optional: false
943
+ };
944
+ }
945
+ if (typeof arg === "object" && arg !== null) return {
946
+ type: "ObjectExpr",
947
+ properties: Object.entries(arg).map(([k, v]) => {
948
+ return {
949
+ key: /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? {
950
+ type: "Identifier",
951
+ name: k
952
+ } : {
953
+ type: "StringLiteral",
954
+ value: k,
955
+ quote: "\""
956
+ },
957
+ value: serializeArgumentToAST(v),
958
+ computed: false,
959
+ shorthand: false
960
+ };
961
+ })
962
+ };
963
+ if (arg === null) return { type: "NullLiteral" };
964
+ if (arg === void 0) return {
965
+ type: "Identifier",
966
+ name: "undefined"
967
+ };
968
+ if (typeof arg === "boolean") return {
969
+ type: "BooleanLiteral",
970
+ value: arg
971
+ };
972
+ if (typeof arg === "number") return {
973
+ type: "NumberLiteral",
974
+ value: arg,
975
+ raw: String(arg)
976
+ };
977
+ if (typeof arg === "string") return {
978
+ type: "StringLiteral",
979
+ value: arg,
980
+ quote: "\""
981
+ };
982
+ if (typeof arg === "bigint") return {
983
+ type: "CallExpr",
984
+ callee: {
985
+ type: "Identifier",
986
+ name: "BigInt"
987
+ },
988
+ arguments: [{
989
+ type: "StringLiteral",
990
+ value: arg.toString(),
991
+ quote: "\""
992
+ }],
993
+ optional: false
994
+ };
995
+ throw new Error(`Unsupported argument type: ${typeof arg}`);
610
996
  }
611
997
  /**
612
998
  * 从参数中收集依赖的 Symbol
@@ -658,53 +1044,85 @@ function createProxyVariable(id) {
658
1044
  * @returns Proxy 包装的 Expression
659
1045
  */
660
1046
  function createProxyExpression(rootId, path, deps) {
661
- const source = `${getVariablePlaceholder$1(rootId)}.${path.join(".")}`;
1047
+ let ast = {
1048
+ type: "Identifier",
1049
+ name: getVariablePlaceholder$1(rootId)
1050
+ };
1051
+ for (const prop of path) ast = {
1052
+ type: "MemberExpr",
1053
+ object: ast,
1054
+ property: {
1055
+ type: "Identifier",
1056
+ name: prop
1057
+ },
1058
+ computed: false,
1059
+ optional: false
1060
+ };
662
1061
  const proxy = new Proxy(function() {}, {
663
1062
  get(_target, prop) {
664
1063
  if (typeof prop === "symbol") return void 0;
665
1064
  return createProxyExpression(rootId, [...path, String(prop)], deps);
666
1065
  },
667
1066
  apply(_target, _thisArg, args) {
668
- const callSource = `${source}(${args.map(serializeArgument).join(", ")})`;
1067
+ const callAst = {
1068
+ type: "CallExpr",
1069
+ callee: ast,
1070
+ arguments: args.map(serializeArgumentToAST),
1071
+ optional: false
1072
+ };
669
1073
  const newDeps = new Set(deps);
670
1074
  collectDepsFromArgs(args, newDeps);
671
- return createProxyExpressionWithSource(callSource, newDeps);
1075
+ return createProxyExpressionWithAST(callAst, newDeps);
672
1076
  }
673
1077
  });
674
1078
  setProxyMetadata(proxy, {
675
1079
  type: "expression",
676
1080
  path,
677
1081
  rootVariable: rootId,
678
- source,
1082
+ ast,
679
1083
  dependencies: deps
680
1084
  });
681
1085
  return proxy;
682
1086
  }
683
1087
  /**
684
- * 创建带完整源码的 Proxy(方法调用后)
1088
+ * 创建带完整 AST 的 Proxy(方法调用后)
685
1089
  * 可以继续链式访问和调用
686
1090
  *
687
- * @param source - 完整的表达式源码
1091
+ * @param ast - 完整的表达式 AST
688
1092
  * @param deps - 依赖集合
689
1093
  * @returns Proxy 包装的 Expression
690
1094
  */
691
- function createProxyExpressionWithSource(source, deps) {
1095
+ function createProxyExpressionWithAST(ast, deps) {
692
1096
  const proxy = new Proxy(function() {}, {
693
1097
  get(_target, prop) {
694
1098
  if (typeof prop === "symbol") return void 0;
695
- return createProxyExpressionWithSource(`(${source}).${String(prop)}`, deps);
1099
+ return createProxyExpressionWithAST({
1100
+ type: "MemberExpr",
1101
+ object: ast,
1102
+ property: {
1103
+ type: "Identifier",
1104
+ name: String(prop)
1105
+ },
1106
+ computed: false,
1107
+ optional: false
1108
+ }, deps);
696
1109
  },
697
1110
  apply(_target, _thisArg, args) {
698
- const callSource = `(${source})(${args.map(serializeArgument).join(", ")})`;
1111
+ const callAst = {
1112
+ type: "CallExpr",
1113
+ callee: ast,
1114
+ arguments: args.map(serializeArgumentToAST),
1115
+ optional: false
1116
+ };
699
1117
  const newDeps = new Set(deps);
700
1118
  collectDepsFromArgs(args, newDeps);
701
- return createProxyExpressionWithSource(callSource, newDeps);
1119
+ return createProxyExpressionWithAST(callAst, newDeps);
702
1120
  }
703
1121
  });
704
1122
  setProxyMetadata(proxy, {
705
1123
  type: "expression",
706
- path: [source],
707
- source,
1124
+ path: [],
1125
+ ast,
708
1126
  dependencies: deps
709
1127
  });
710
1128
  return proxy;
@@ -770,32 +1188,31 @@ const ALLOWED_GLOBALS = new Set([
770
1188
  "isNaN",
771
1189
  "isFinite",
772
1190
  "parseInt",
773
- "parseFloat"
1191
+ "parseFloat",
1192
+ "BigInt",
1193
+ "URL",
1194
+ "URLSearchParams",
1195
+ "Map",
1196
+ "Set",
1197
+ "Int8Array",
1198
+ "Uint8Array",
1199
+ "Uint8ClampedArray",
1200
+ "Int16Array",
1201
+ "Uint16Array",
1202
+ "Int32Array",
1203
+ "Uint32Array",
1204
+ "Float32Array",
1205
+ "Float64Array",
1206
+ "BigInt64Array",
1207
+ "BigUint64Array",
1208
+ "ArrayBuffer",
1209
+ "DataView"
774
1210
  ]);
775
1211
  /**
776
- * 将占位符替换为实际变量名
777
- *
778
- * @param source - 包含占位符的表达式源码
779
- * @param context - 变量名到值的映射
780
- * @returns 替换后的源码
781
- */
782
- function preprocessExpression(source, context) {
783
- const descToName = /* @__PURE__ */ new Map();
784
- for (const [name, value] of Object.entries(context)) {
785
- const id = getVariableId(value);
786
- if (id && id.description) descToName.set(id.description, name);
787
- }
788
- return source.replace(/\$\$VAR_([^$]+)\$\$/g, (match, desc) => {
789
- const name = descToName.get(desc);
790
- if (!name) throw new Error(`Unknown variable placeholder: ${match}`);
791
- return name;
792
- });
793
- }
794
- /**
795
1212
  * 将 Proxy Expression 编译为可序列化的 JSON 结构
796
1213
  *
797
1214
  * @template TResult - 表达式结果类型
798
- * @param expression - Proxy Expression
1215
+ * @param expression - Proxy Expression,或包含 Proxy 的对象/数组/原始值
799
1216
  * @param variables - 所有使用的变量定义
800
1217
  * @param options - 编译选项
801
1218
  * @returns 编译后的数据结构 [变量名列表, 表达式1, 表达式2, ...]
@@ -814,25 +1231,31 @@ function preprocessExpression(source, context) {
814
1231
  */
815
1232
  function compile(expression, variables, options = {}) {
816
1233
  const { shortCircuit = true } = options;
817
- const meta = getProxyMetadata(expression);
818
- if (!meta?.source) throw new Error("Invalid expression: expected a Proxy Expression");
819
- const source = preprocessExpression(meta.source, variables);
1234
+ const ast = serializeArgumentToAST(expression);
820
1235
  const variableOrder = [];
821
1236
  const variableToIndex = /* @__PURE__ */ new Map();
822
1237
  for (const name of Object.keys(variables)) if (!variableToIndex.has(name)) {
823
1238
  variableToIndex.set(name, variableOrder.length);
824
1239
  variableOrder.push(name);
825
1240
  }
826
- const ast = parse(source);
1241
+ const descToName = /* @__PURE__ */ new Map();
1242
+ for (const [name, value] of Object.entries(variables)) {
1243
+ const id = getVariableId(value);
1244
+ if (id && id.description) descToName.set(id.description, name);
1245
+ }
827
1246
  const undefinedVars = [];
828
1247
  const transformed = transformIdentifiers(ast, (name) => {
1248
+ const placeholderMatch = name.match(/^\$\$VAR_(.+)\$\$$/);
1249
+ if (placeholderMatch) {
1250
+ const desc = placeholderMatch[1];
1251
+ const varName = descToName.get(desc);
1252
+ if (!varName) throw new Error(`Unknown variable placeholder: ${name}`);
1253
+ name = varName;
1254
+ }
829
1255
  const index = variableToIndex.get(name);
830
- if (index !== void 0) return {
831
- type: "Identifier",
832
- name: `$${index}`
833
- };
1256
+ if (index !== void 0) return `$${index}`;
834
1257
  if (!ALLOWED_GLOBALS.has(name)) undefinedVars.push(name);
835
- return null;
1258
+ return name;
836
1259
  });
837
1260
  if (undefinedVars.length > 0) throw new Error(`Undefined variable(s): ${[...new Set(undefinedVars)].join(", ")}`);
838
1261
  const expressions = [];
@@ -897,56 +1320,6 @@ function compile(expression, variables, options = {}) {
897
1320
  } else expressions.push(generate(transformed));
898
1321
  return [variableOrder, ...expressions];
899
1322
  }
900
- /**
901
- * 将 AST 中的标识符替换为对应的 AST 节点
902
- *
903
- * @param node - 要转换的 AST 节点
904
- * @param getReplacementAst - 根据标识符名称返回替换的 AST 节点,返回 null 表示不替换
905
- * @returns 转换后的 AST 节点
906
- */
907
- function transformIdentifiers(node, getReplacementAst) {
908
- switch (node.type) {
909
- case "Identifier": return getReplacementAst(node.name) ?? node;
910
- case "BinaryExpr": return {
911
- ...node,
912
- left: transformIdentifiers(node.left, getReplacementAst),
913
- right: transformIdentifiers(node.right, getReplacementAst)
914
- };
915
- case "UnaryExpr": return {
916
- ...node,
917
- argument: transformIdentifiers(node.argument, getReplacementAst)
918
- };
919
- case "ConditionalExpr": return {
920
- ...node,
921
- test: transformIdentifiers(node.test, getReplacementAst),
922
- consequent: transformIdentifiers(node.consequent, getReplacementAst),
923
- alternate: transformIdentifiers(node.alternate, getReplacementAst)
924
- };
925
- case "MemberExpr": return {
926
- ...node,
927
- object: transformIdentifiers(node.object, getReplacementAst),
928
- property: node.computed ? transformIdentifiers(node.property, getReplacementAst) : node.property
929
- };
930
- case "CallExpr": return {
931
- ...node,
932
- callee: transformIdentifiers(node.callee, getReplacementAst),
933
- arguments: node.arguments.map((arg) => transformIdentifiers(arg, getReplacementAst))
934
- };
935
- case "ArrayExpr": return {
936
- ...node,
937
- elements: node.elements.map((el) => transformIdentifiers(el, getReplacementAst))
938
- };
939
- case "ObjectExpr": return {
940
- ...node,
941
- properties: node.properties.map((prop) => ({
942
- ...prop,
943
- key: prop.computed ? transformIdentifiers(prop.key, getReplacementAst) : prop.key,
944
- value: transformIdentifiers(prop.value, getReplacementAst)
945
- }))
946
- };
947
- default: return node;
948
- }
949
- }
950
1323
 
951
1324
  //#endregion
952
1325
  //#region src/evaluate.ts
@@ -1105,7 +1478,11 @@ function expr(context) {
1105
1478
  const deps = /* @__PURE__ */ new Set();
1106
1479
  const nameToId = /* @__PURE__ */ new Map();
1107
1480
  for (const [name, value] of Object.entries(context)) {
1108
- const id = getVariableId(value);
1481
+ let id = getVariableId(value);
1482
+ if (!id && (typeof value === "object" || typeof value === "function") && value !== null) {
1483
+ const meta = getProxyMetadata(value);
1484
+ if (meta?.type === "variable" && meta.rootVariable) id = meta.rootVariable;
1485
+ }
1109
1486
  if (id) {
1110
1487
  deps.add(id);
1111
1488
  nameToId.set(name, id);
@@ -1114,33 +1491,115 @@ function expr(context) {
1114
1491
  if (meta?.dependencies) for (const dep of meta.dependencies) deps.add(dep);
1115
1492
  }
1116
1493
  }
1117
- let transformedSource = source;
1118
- for (const [name, id] of nameToId) {
1119
- const escapedPlaceholder = getVariablePlaceholder(id).replace(/\$/g, "$$$$");
1120
- const regex = new RegExp(`\\b${name}\\b`, "g");
1121
- transformedSource = transformedSource.replace(regex, escapedPlaceholder);
1122
- }
1123
- for (const [name, value] of Object.entries(context)) if ((typeof value === "object" || typeof value === "function") && value !== null && !getVariableId(value)) {
1494
+ const nameToExprAST = /* @__PURE__ */ new Map();
1495
+ for (const [name, value] of Object.entries(context)) if ((typeof value === "object" || typeof value === "function") && value !== null) {
1496
+ if (nameToId.has(name)) continue;
1124
1497
  const meta = getProxyMetadata(value);
1125
- if (meta?.source) {
1126
- const regex = new RegExp(`\\b${name}\\b`, "g");
1127
- const escapedSource = `(${meta.source})`.replace(/\$/g, "$$$$");
1128
- transformedSource = transformedSource.replace(regex, escapedSource);
1129
- }
1498
+ if (meta?.ast) nameToExprAST.set(name, meta.ast);
1130
1499
  }
1131
- return createProxyExpressionWithSource(transformedSource, deps);
1500
+ return createProxyExpressionWithAST(transformIdentifiers(parse(source), (name) => {
1501
+ const id = nameToId.get(name);
1502
+ if (id) return getVariablePlaceholder(id);
1503
+ const exprAST = nameToExprAST.get(name);
1504
+ if (exprAST) return exprAST;
1505
+ return name;
1506
+ }), deps);
1132
1507
  };
1133
1508
  }
1134
1509
 
1135
1510
  //#endregion
1136
- //#region src/template.ts
1511
+ //#region src/lambda.ts
1512
+ /**
1513
+ * Lambda 参数计数器,用于生成唯一 ID
1514
+ */
1515
+ let lambdaParamCounter = 0;
1137
1516
  /**
1138
- * 转义字符串字面量中的特殊字符
1517
+ * Lambda 参数到索引的映射
1518
+ * 用于编译时确定参数位置
1139
1519
  */
1140
- function escapeStringLiteral(str) {
1141
- return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1520
+ const lambdaParamIndices = /* @__PURE__ */ new WeakMap();
1521
+ /**
1522
+ * 创建 lambda 参数代理
1523
+ * 生成带特殊标记的 Proxy,用于在表达式中追踪参数
1524
+ *
1525
+ * @param index - 参数索引(0, 1, 2...)
1526
+ * @returns Lambda 参数代理
1527
+ */
1528
+ function createLambdaParam(index) {
1529
+ const proxy = createProxyVariable(Symbol(`lambda_param_${lambdaParamCounter++}_${index}`));
1530
+ lambdaParamIndices.set(proxy, index);
1531
+ return proxy;
1142
1532
  }
1143
1533
  /**
1534
+ * 创建类型安全的 lambda 表达式
1535
+ *
1536
+ * @template Args - 参数类型元组
1537
+ * @template R - 返回值类型
1538
+ * @param builder - Lambda 构建函数,接收参数代理,返回函数体表达式
1539
+ * @returns Lambda 表达式代理
1540
+ *
1541
+ * @example
1542
+ * ```ts
1543
+ * const add = lambda<[number, number], number>(
1544
+ * (a, b) => expr({ a, b })("a + b")
1545
+ * );
1546
+ *
1547
+ * const numbers = variable<number[]>();
1548
+ * const sum = numbers.reduce(add, 0);
1549
+ * ```
1550
+ */
1551
+ function lambda(builder) {
1552
+ const paramCount = builder.length;
1553
+ const params = [];
1554
+ const paramSymbols = [];
1555
+ for (let i = 0; i < paramCount; i++) {
1556
+ const param = createLambdaParam(i);
1557
+ params.push(param);
1558
+ const meta = getProxyMetadata(param);
1559
+ if (meta?.rootVariable) paramSymbols.push(meta.rootVariable);
1560
+ }
1561
+ const bodyExpr = builder(...params);
1562
+ let bodyAst;
1563
+ let bodyDeps;
1564
+ const meta = (typeof bodyExpr === "object" || typeof bodyExpr === "function") && bodyExpr !== null ? getProxyMetadata(bodyExpr) : void 0;
1565
+ if (meta?.ast) {
1566
+ bodyAst = meta.ast;
1567
+ bodyDeps = meta.dependencies ?? /* @__PURE__ */ new Set();
1568
+ } else {
1569
+ bodyAst = serializeArgumentToAST(bodyExpr);
1570
+ bodyDeps = /* @__PURE__ */ new Set();
1571
+ collectDepsFromArgs([bodyExpr], bodyDeps);
1572
+ }
1573
+ const transformedBodyAst = transformIdentifiers(bodyAst, (name) => {
1574
+ for (let i = 0; i < paramSymbols.length; i++) {
1575
+ const sym = paramSymbols[i];
1576
+ if (!sym) continue;
1577
+ if (name === `$$VAR_${sym.description}$$`) return `_${i}`;
1578
+ }
1579
+ return name;
1580
+ });
1581
+ const arrowFunctionAst = {
1582
+ type: "ArrowFunctionExpr",
1583
+ params: params.map((_, i) => ({
1584
+ type: "Identifier",
1585
+ name: `_${i}`
1586
+ })),
1587
+ body: transformedBodyAst
1588
+ };
1589
+ const closureDeps = /* @__PURE__ */ new Set();
1590
+ for (const dep of bodyDeps) if (!paramSymbols.includes(dep)) closureDeps.add(dep);
1591
+ const lambdaProxy = createProxyExpressionWithAST(arrowFunctionAst, closureDeps);
1592
+ const existingMeta = getProxyMetadata(lambdaProxy);
1593
+ if (existingMeta) setProxyMetadata(lambdaProxy, {
1594
+ ...existingMeta,
1595
+ type: "expression"
1596
+ });
1597
+ return lambdaProxy;
1598
+ }
1599
+
1600
+ //#endregion
1601
+ //#region src/template.ts
1602
+ /**
1144
1603
  * Tagged template 函数,用于创建包含变量的字符串表达式
1145
1604
  *
1146
1605
  * @example
@@ -1161,17 +1620,62 @@ function t(strings, ...values) {
1161
1620
  const parts = [];
1162
1621
  for (let i = 0; i < strings.length; i++) {
1163
1622
  const str = strings[i];
1164
- if (str.length > 0) parts.push(`"${escapeStringLiteral(str)}"`);
1623
+ if (str.length > 0) parts.push({
1624
+ type: "StringLiteral",
1625
+ value: str,
1626
+ quote: "\""
1627
+ });
1165
1628
  if (i < values.length) {
1166
- const serialized = serializeArgument(values[i]);
1167
- parts.push(serialized);
1629
+ const ast = serializeArgumentToAST(values[i]);
1630
+ parts.push(ast);
1168
1631
  }
1169
1632
  }
1170
- if (parts.length === 0) return createProxyExpressionWithSource("\"\"", deps);
1171
- if (parts.length === 1) return createProxyExpressionWithSource(parts[0], deps);
1172
- return createProxyExpressionWithSource("(" + parts.join(" + ") + ")", deps);
1633
+ if (parts.length === 0) return createProxyExpressionWithAST({
1634
+ type: "StringLiteral",
1635
+ value: "",
1636
+ quote: "\""
1637
+ }, deps);
1638
+ if (parts.length === 1) return createProxyExpressionWithAST(parts[0], deps);
1639
+ let resultAst = parts[0];
1640
+ for (let i = 1; i < parts.length; i++) resultAst = {
1641
+ type: "BinaryExpr",
1642
+ operator: "+",
1643
+ left: resultAst,
1644
+ right: parts[i]
1645
+ };
1646
+ return createProxyExpressionWithAST(resultAst, deps);
1647
+ }
1648
+
1649
+ //#endregion
1650
+ //#region src/wrap.ts
1651
+ /**
1652
+ * 将静态值包装为 Proxy Expression
1653
+ * 返回的 Proxy 可以像 Variable 一样调用方法和访问属性
1654
+ *
1655
+ * @template T - 值的类型
1656
+ * @param value - 要包装的静态值(支持原始值、对象、数组、Date、RegExp 等)
1657
+ * @returns Proxy Expression,可以继续链式调用
1658
+ *
1659
+ * @example
1660
+ * ```ts
1661
+ * // 包装 RegExp
1662
+ * const pattern = wrap(/^[a-z]+$/i);
1663
+ * const input = variable<string>();
1664
+ * const result = pattern.match(input);
1665
+ *
1666
+ * // 包装 Date
1667
+ * const now = wrap(new Date());
1668
+ * const year = now.getFullYear();
1669
+ *
1670
+ * // 包装数组
1671
+ * const numbers = wrap([1, 2, 3, 4, 5]);
1672
+ * const doubled = numbers.map((x) => x * 2);
1673
+ * ```
1674
+ */
1675
+ function wrap(value) {
1676
+ return createProxyExpressionWithAST(serializeArgumentToAST(value), /* @__PURE__ */ new Set());
1173
1677
  }
1174
1678
 
1175
1679
  //#endregion
1176
- export { compile, evaluate, expr, getVariableId, isProxy, isProxyExpression, isProxyVariable, t, variable };
1680
+ export { compile, evaluate, expr, getVariableId, isProxy, isProxyExpression, isProxyVariable, lambda, t, variable, wrap };
1177
1681
  //# sourceMappingURL=index.mjs.map