@ricsam/formula-engine 0.2.9 → 0.2.10

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.
Files changed (41) hide show
  1. package/README.md +40 -0
  2. package/dist/cjs/core/engine.cjs +37 -1
  3. package/dist/cjs/core/engine.cjs.map +3 -3
  4. package/dist/cjs/core/managers/dependency-manager.cjs +151 -67
  5. package/dist/cjs/core/managers/dependency-manager.cjs.map +3 -3
  6. package/dist/cjs/core/managers/workbook-manager.cjs +197 -1
  7. package/dist/cjs/core/managers/workbook-manager.cjs.map +3 -3
  8. package/dist/cjs/core/types.cjs.map +2 -2
  9. package/dist/cjs/core/workbook-renamer.cjs.map +1 -1
  10. package/dist/cjs/evaluator/formula-evaluator.cjs +22 -48
  11. package/dist/cjs/evaluator/formula-evaluator.cjs.map +3 -3
  12. package/dist/cjs/functions/math/sum/summation-utils.cjs +2 -2
  13. package/dist/cjs/functions/math/sum/summation-utils.cjs.map +2 -2
  14. package/dist/cjs/package.json +1 -1
  15. package/dist/cjs/parser/formatter.cjs +3 -4
  16. package/dist/cjs/parser/formatter.cjs.map +3 -3
  17. package/dist/cjs/parser/parser.cjs +24 -216
  18. package/dist/cjs/parser/parser.cjs.map +3 -3
  19. package/dist/mjs/core/engine.mjs +37 -1
  20. package/dist/mjs/core/engine.mjs.map +3 -3
  21. package/dist/mjs/core/managers/dependency-manager.mjs +151 -67
  22. package/dist/mjs/core/managers/dependency-manager.mjs.map +3 -3
  23. package/dist/mjs/core/managers/workbook-manager.mjs +197 -1
  24. package/dist/mjs/core/managers/workbook-manager.mjs.map +3 -3
  25. package/dist/mjs/core/types.mjs.map +2 -2
  26. package/dist/mjs/core/workbook-renamer.mjs.map +1 -1
  27. package/dist/mjs/evaluator/formula-evaluator.mjs +22 -48
  28. package/dist/mjs/evaluator/formula-evaluator.mjs.map +3 -3
  29. package/dist/mjs/functions/math/sum/summation-utils.mjs +2 -2
  30. package/dist/mjs/functions/math/sum/summation-utils.mjs.map +2 -2
  31. package/dist/mjs/package.json +1 -1
  32. package/dist/mjs/parser/formatter.mjs +3 -4
  33. package/dist/mjs/parser/formatter.mjs.map +3 -3
  34. package/dist/mjs/parser/parser.mjs +24 -216
  35. package/dist/mjs/parser/parser.mjs.map +3 -3
  36. package/dist/types/core/engine.d.ts +15 -1
  37. package/dist/types/core/managers/dependency-manager.d.ts +4 -0
  38. package/dist/types/core/managers/workbook-manager.d.ts +23 -1
  39. package/dist/types/core/types.d.ts +35 -0
  40. package/dist/types/parser/parser.d.ts +4 -6
  41. package/package.json +1 -1
@@ -75,6 +75,9 @@ class Parser {
75
75
  }
76
76
  if (token.type === "RBRACKET") {
77
77
  const nextToken = this.tokens.peekAhead(pos + 1);
78
+ if (nextToken?.type === "EXCLAMATION") {
79
+ return { isWorkbookReference: true };
80
+ }
78
81
  if (nextToken && nextToken.type === "IDENTIFIER") {
79
82
  let lookPos = pos + 2;
80
83
  while (lookPos < pos + 10) {
@@ -117,6 +120,10 @@ class Parser {
117
120
  }
118
121
  const workbookName = this.input.substring(workbookStartPos, workbookEndPos);
119
122
  this.tokens.consume();
123
+ if (this.tokens.match("EXCLAMATION")) {
124
+ this.tokens.consume();
125
+ return this.parseWorkbookQualifiedTableReference(workbookName, start);
126
+ }
120
127
  if (!this.tokens.match("IDENTIFIER")) {
121
128
  throw new ParseError("Expected sheet name after workbook reference", this.tokens.peek().position);
122
129
  }
@@ -179,6 +186,16 @@ class Parser {
179
186
  });
180
187
  }
181
188
  }
189
+ parseWorkbookQualifiedTableReference(workbookName, startPos) {
190
+ if (!this.tokens.match("IDENTIFIER")) {
191
+ throw new ParseError(`Expected table name after [${workbookName}]!`, this.tokens.peek().position);
192
+ }
193
+ const tableName = this.tokens.consume().value;
194
+ if (!this.tokens.match("LBRACKET")) {
195
+ throw new ParseError(`Workbook-qualified references without a sheet only support tables. Use [${workbookName}]!${tableName}[...] for tables or [${workbookName}]Sheet!A1 for sheet references`, this.tokens.peek().position);
196
+ }
197
+ return this.parseTableReference(tableName, startPos, { workbookName });
198
+ }
182
199
  parseBareColumnReference() {
183
200
  const startPos = this.tokens.peek().position.start;
184
201
  this.tokens.consume();
@@ -693,7 +710,8 @@ class Parser {
693
710
  sheetName: parsed.sheet
694
711
  });
695
712
  }
696
- parseTableReference(tableName, startPos) {
713
+ parseTableReference(tableName, startPos, qualifiers) {
714
+ const { sheetName, workbookName } = qualifiers ?? {};
697
715
  this.tokens.consume();
698
716
  let selector;
699
717
  let cols;
@@ -891,6 +909,8 @@ class Parser {
891
909
  }
892
910
  return import_ast.createStructuredReferenceNode({
893
911
  tableName,
912
+ sheetName,
913
+ workbookName,
894
914
  cols,
895
915
  selector,
896
916
  isCurrentRow,
@@ -901,212 +921,7 @@ class Parser {
901
921
  });
902
922
  }
903
923
  parseTableReferenceWithSheet(tableName, sheetName, startPos) {
904
- this.tokens.consume();
905
- let selector;
906
- let cols;
907
- let isCurrentRow = false;
908
- if (this.tokens.match("LBRACKET")) {
909
- this.tokens.consume();
910
- if (this.tokens.match("HASH")) {
911
- this.tokens.consume();
912
- if (!this.tokens.match("IDENTIFIER")) {
913
- throw new ParseError("Expected selector name after #", this.tokens.peek().position);
914
- }
915
- const selectorName = this.tokens.consume().value;
916
- selector = `#${selectorName}`;
917
- if (!this.tokens.match("RBRACKET")) {
918
- throw new ParseError("Expected ] after selector", this.tokens.peek().position);
919
- }
920
- this.tokens.consume();
921
- if (this.tokens.match("COMMA")) {
922
- this.tokens.consume();
923
- if (!this.tokens.match("LBRACKET")) {
924
- throw new ParseError("Expected [ after comma", this.tokens.peek().position);
925
- }
926
- this.tokens.consume();
927
- if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
928
- throw new ParseError("Expected column name", this.tokens.peek().position);
929
- }
930
- const colStart = this.parseColumnName();
931
- if (this.tokens.match("COLON") && !this.tokens.peekAhead(1)?.type.match(/RBRACKET/)) {
932
- this.tokens.consume();
933
- if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
934
- throw new ParseError("Expected end column name after :", this.tokens.peek().position);
935
- }
936
- const colEnd = this.parseColumnName();
937
- cols = {
938
- startCol: colStart,
939
- endCol: colEnd
940
- };
941
- } else {
942
- cols = {
943
- startCol: colStart,
944
- endCol: colStart
945
- };
946
- }
947
- if (!this.tokens.match("RBRACKET")) {
948
- throw new ParseError("Expected ] after column specification", this.tokens.peek().position);
949
- }
950
- this.tokens.consume();
951
- if (this.tokens.match("COLON")) {
952
- this.tokens.consume();
953
- if (!this.tokens.match("LBRACKET")) {
954
- throw new ParseError("Expected [ after : in column range", this.tokens.peek().position);
955
- }
956
- this.tokens.consume();
957
- if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
958
- throw new ParseError("Expected end column name", this.tokens.peek().position);
959
- }
960
- const colEnd = this.parseColumnName();
961
- if (!this.tokens.match("RBRACKET")) {
962
- throw new ParseError("Expected ] after end column name", this.tokens.peek().position);
963
- }
964
- this.tokens.consume();
965
- cols = {
966
- startCol: colStart,
967
- endCol: colEnd
968
- };
969
- }
970
- }
971
- if (!this.tokens.match("RBRACKET")) {
972
- throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
973
- }
974
- this.tokens.consume();
975
- } else if (this.tokens.match("IDENTIFIER") || this.tokens.match("FUNCTION") || this.tokens.match("NUMBER") || this.tokens.match("OPERATOR")) {
976
- const colStart = this.parseColumnName();
977
- if (!this.tokens.match("RBRACKET")) {
978
- throw new ParseError("Expected ] after column name", this.tokens.peek().position);
979
- }
980
- this.tokens.consume();
981
- if (this.tokens.match("COLON")) {
982
- this.tokens.consume();
983
- if (!this.tokens.match("LBRACKET")) {
984
- throw new ParseError("Expected [ before second column name", this.tokens.peek().position);
985
- }
986
- this.tokens.consume();
987
- const colEnd = this.parseColumnName();
988
- if (!this.tokens.match("RBRACKET")) {
989
- throw new ParseError("Expected ] after second column name", this.tokens.peek().position);
990
- }
991
- this.tokens.consume();
992
- if (!this.tokens.match("RBRACKET")) {
993
- throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
994
- }
995
- this.tokens.consume();
996
- cols = {
997
- startCol: colStart,
998
- endCol: colEnd
999
- };
1000
- } else {
1001
- if (!this.tokens.match("RBRACKET")) {
1002
- throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
1003
- }
1004
- this.tokens.consume();
1005
- cols = {
1006
- startCol: colStart,
1007
- endCol: colStart
1008
- };
1009
- }
1010
- }
1011
- } else if (this.tokens.match("AT")) {
1012
- this.tokens.consume();
1013
- isCurrentRow = true;
1014
- if (this.tokens.match("LBRACKET")) {
1015
- this.tokens.consume();
1016
- const colStart = this.parseColumnName();
1017
- if (!this.tokens.match("RBRACKET")) {
1018
- throw new ParseError("Expected ] after column name", this.tokens.peek().position);
1019
- }
1020
- this.tokens.consume();
1021
- if (this.tokens.match("COLON")) {
1022
- this.tokens.consume();
1023
- if (!this.tokens.match("LBRACKET")) {
1024
- throw new ParseError("Expected [ before second column name", this.tokens.peek().position);
1025
- }
1026
- this.tokens.consume();
1027
- const colEnd = this.parseColumnName();
1028
- if (!this.tokens.match("RBRACKET")) {
1029
- throw new ParseError("Expected ] after second column name", this.tokens.peek().position);
1030
- }
1031
- this.tokens.consume();
1032
- cols = {
1033
- startCol: colStart,
1034
- endCol: colEnd
1035
- };
1036
- } else {
1037
- cols = {
1038
- startCol: colStart,
1039
- endCol: colStart
1040
- };
1041
- }
1042
- if (!this.tokens.match("RBRACKET")) {
1043
- throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
1044
- }
1045
- this.tokens.consume();
1046
- } else {
1047
- const colStart = this.parseColumnName();
1048
- if (this.tokens.match("COLON")) {
1049
- this.tokens.consume();
1050
- const colEnd = this.parseColumnName();
1051
- cols = {
1052
- startCol: colStart,
1053
- endCol: colEnd
1054
- };
1055
- } else {
1056
- cols = {
1057
- startCol: colStart,
1058
- endCol: colStart
1059
- };
1060
- }
1061
- if (!this.tokens.match("RBRACKET")) {
1062
- throw new ParseError("Expected ] after column reference", this.tokens.peek().position);
1063
- }
1064
- this.tokens.consume();
1065
- }
1066
- } else if (this.tokens.match("IDENTIFIER") || this.tokens.match("FUNCTION") || this.tokens.match("NUMBER") || this.tokens.match("OPERATOR")) {
1067
- const colStart = this.parseColumnName();
1068
- if (this.tokens.match("COLON")) {
1069
- this.tokens.consume();
1070
- const colEnd = this.parseColumnName();
1071
- cols = {
1072
- startCol: colStart,
1073
- endCol: colEnd
1074
- };
1075
- } else {
1076
- cols = {
1077
- startCol: colStart,
1078
- endCol: colStart
1079
- };
1080
- }
1081
- if (!this.tokens.match("RBRACKET")) {
1082
- throw new ParseError("Expected ] after column name", this.tokens.peek().position);
1083
- }
1084
- this.tokens.consume();
1085
- } else if (this.tokens.match("HASH")) {
1086
- this.tokens.consume();
1087
- if (!this.tokens.match("IDENTIFIER")) {
1088
- throw new ParseError("Expected selector name after #", this.tokens.peek().position);
1089
- }
1090
- const selectorName = this.tokens.consume().value;
1091
- selector = `#${selectorName}`;
1092
- if (!this.tokens.match("RBRACKET")) {
1093
- throw new ParseError("Expected ] after selector", this.tokens.peek().position);
1094
- }
1095
- this.tokens.consume();
1096
- } else {
1097
- throw new ParseError("Expected column name or selector in table reference", this.tokens.peek().position);
1098
- }
1099
- return import_ast.createStructuredReferenceNode({
1100
- tableName,
1101
- sheetName,
1102
- cols,
1103
- selector,
1104
- isCurrentRow,
1105
- position: {
1106
- start: startPos,
1107
- end: this.tokens.peek().position?.end ?? 0
1108
- }
1109
- });
924
+ return this.parseTableReference(tableName, startPos, { sheetName });
1110
925
  }
1111
926
  parseCellOrRangeAfterSheets() {
1112
927
  const start = this.tokens.peek().position?.start ?? 0;
@@ -1280,13 +1095,6 @@ class Parser {
1280
1095
  rangeNode.workbookName = workbookName;
1281
1096
  return rangeNode;
1282
1097
  }
1283
- parseTableReferenceWithWorkbookAndSheet(tableName, workbookName, sheetName, startPos) {
1284
- const tableRef = this.parseTableReferenceWithSheet(tableName, sheetName, startPos);
1285
- if (tableRef.type === "structured-reference") {
1286
- tableRef.workbookName = workbookName;
1287
- }
1288
- return tableRef;
1289
- }
1290
1098
  parseRange(startRef, endRef, startPos, endPos) {
1291
1099
  let fullRange = `${startRef}:${endRef}`;
1292
1100
  let sheetName;
@@ -1598,7 +1406,7 @@ class Parser {
1598
1406
  const identifier2 = this.tokens.consume();
1599
1407
  ref += identifier2.value;
1600
1408
  if (this.tokens.match("LBRACKET")) {
1601
- return this.parseTableReferenceWithWorkbookAndSheet(identifier2.value, workbookName, sheetName, startPos);
1409
+ throw new ParseError(`Cross-workbook table references use [${workbookName}]!${identifier2.value}[...] instead of [${workbookName}]${sheetName}!${identifier2.value}[...]`, this.tokens.peek().position);
1602
1410
  }
1603
1411
  if (this.tokens.match("COLON")) {
1604
1412
  this.tokens.consume();
@@ -1707,4 +1515,4 @@ function parseFormula(formula) {
1707
1515
  return Parser.parse(formula);
1708
1516
  }
1709
1517
 
1710
- //# debugId=BE109E0931441DC764756E2164756E21
1518
+ //# debugId=B6F0346D15B1EFC164756E2164756E21