@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.
- package/README.md +40 -0
- package/dist/cjs/core/engine.cjs +37 -1
- package/dist/cjs/core/engine.cjs.map +3 -3
- package/dist/cjs/core/managers/dependency-manager.cjs +151 -67
- package/dist/cjs/core/managers/dependency-manager.cjs.map +3 -3
- package/dist/cjs/core/managers/workbook-manager.cjs +197 -1
- package/dist/cjs/core/managers/workbook-manager.cjs.map +3 -3
- package/dist/cjs/core/types.cjs.map +2 -2
- package/dist/cjs/core/workbook-renamer.cjs.map +1 -1
- package/dist/cjs/evaluator/formula-evaluator.cjs +22 -48
- package/dist/cjs/evaluator/formula-evaluator.cjs.map +3 -3
- package/dist/cjs/functions/math/sum/summation-utils.cjs +2 -2
- package/dist/cjs/functions/math/sum/summation-utils.cjs.map +2 -2
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/parser/formatter.cjs +3 -4
- package/dist/cjs/parser/formatter.cjs.map +3 -3
- package/dist/cjs/parser/parser.cjs +24 -216
- package/dist/cjs/parser/parser.cjs.map +3 -3
- package/dist/mjs/core/engine.mjs +37 -1
- package/dist/mjs/core/engine.mjs.map +3 -3
- package/dist/mjs/core/managers/dependency-manager.mjs +151 -67
- package/dist/mjs/core/managers/dependency-manager.mjs.map +3 -3
- package/dist/mjs/core/managers/workbook-manager.mjs +197 -1
- package/dist/mjs/core/managers/workbook-manager.mjs.map +3 -3
- package/dist/mjs/core/types.mjs.map +2 -2
- package/dist/mjs/core/workbook-renamer.mjs.map +1 -1
- package/dist/mjs/evaluator/formula-evaluator.mjs +22 -48
- package/dist/mjs/evaluator/formula-evaluator.mjs.map +3 -3
- package/dist/mjs/functions/math/sum/summation-utils.mjs +2 -2
- package/dist/mjs/functions/math/sum/summation-utils.mjs.map +2 -2
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/parser/formatter.mjs +3 -4
- package/dist/mjs/parser/formatter.mjs.map +3 -3
- package/dist/mjs/parser/parser.mjs +24 -216
- package/dist/mjs/parser/parser.mjs.map +3 -3
- package/dist/types/core/engine.d.ts +15 -1
- package/dist/types/core/managers/dependency-manager.d.ts +4 -0
- package/dist/types/core/managers/workbook-manager.d.ts +23 -1
- package/dist/types/core/types.d.ts +35 -0
- package/dist/types/parser/parser.d.ts +4 -6
- package/package.json +1 -1
|
@@ -52,6 +52,9 @@ class Parser {
|
|
|
52
52
|
}
|
|
53
53
|
if (token.type === "RBRACKET") {
|
|
54
54
|
const nextToken = this.tokens.peekAhead(pos + 1);
|
|
55
|
+
if (nextToken?.type === "EXCLAMATION") {
|
|
56
|
+
return { isWorkbookReference: true };
|
|
57
|
+
}
|
|
55
58
|
if (nextToken && nextToken.type === "IDENTIFIER") {
|
|
56
59
|
let lookPos = pos + 2;
|
|
57
60
|
while (lookPos < pos + 10) {
|
|
@@ -94,6 +97,10 @@ class Parser {
|
|
|
94
97
|
}
|
|
95
98
|
const workbookName = this.input.substring(workbookStartPos, workbookEndPos);
|
|
96
99
|
this.tokens.consume();
|
|
100
|
+
if (this.tokens.match("EXCLAMATION")) {
|
|
101
|
+
this.tokens.consume();
|
|
102
|
+
return this.parseWorkbookQualifiedTableReference(workbookName, start);
|
|
103
|
+
}
|
|
97
104
|
if (!this.tokens.match("IDENTIFIER")) {
|
|
98
105
|
throw new ParseError("Expected sheet name after workbook reference", this.tokens.peek().position);
|
|
99
106
|
}
|
|
@@ -156,6 +163,16 @@ class Parser {
|
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
165
|
}
|
|
166
|
+
parseWorkbookQualifiedTableReference(workbookName, startPos) {
|
|
167
|
+
if (!this.tokens.match("IDENTIFIER")) {
|
|
168
|
+
throw new ParseError(`Expected table name after [${workbookName}]!`, this.tokens.peek().position);
|
|
169
|
+
}
|
|
170
|
+
const tableName = this.tokens.consume().value;
|
|
171
|
+
if (!this.tokens.match("LBRACKET")) {
|
|
172
|
+
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);
|
|
173
|
+
}
|
|
174
|
+
return this.parseTableReference(tableName, startPos, { workbookName });
|
|
175
|
+
}
|
|
159
176
|
parseBareColumnReference() {
|
|
160
177
|
const startPos = this.tokens.peek().position.start;
|
|
161
178
|
this.tokens.consume();
|
|
@@ -670,7 +687,8 @@ class Parser {
|
|
|
670
687
|
sheetName: parsed.sheet
|
|
671
688
|
});
|
|
672
689
|
}
|
|
673
|
-
parseTableReference(tableName, startPos) {
|
|
690
|
+
parseTableReference(tableName, startPos, qualifiers) {
|
|
691
|
+
const { sheetName, workbookName } = qualifiers ?? {};
|
|
674
692
|
this.tokens.consume();
|
|
675
693
|
let selector;
|
|
676
694
|
let cols;
|
|
@@ -868,6 +886,8 @@ class Parser {
|
|
|
868
886
|
}
|
|
869
887
|
return createStructuredReferenceNode({
|
|
870
888
|
tableName,
|
|
889
|
+
sheetName,
|
|
890
|
+
workbookName,
|
|
871
891
|
cols,
|
|
872
892
|
selector,
|
|
873
893
|
isCurrentRow,
|
|
@@ -878,212 +898,7 @@ class Parser {
|
|
|
878
898
|
});
|
|
879
899
|
}
|
|
880
900
|
parseTableReferenceWithSheet(tableName, sheetName, startPos) {
|
|
881
|
-
this.
|
|
882
|
-
let selector;
|
|
883
|
-
let cols;
|
|
884
|
-
let isCurrentRow = false;
|
|
885
|
-
if (this.tokens.match("LBRACKET")) {
|
|
886
|
-
this.tokens.consume();
|
|
887
|
-
if (this.tokens.match("HASH")) {
|
|
888
|
-
this.tokens.consume();
|
|
889
|
-
if (!this.tokens.match("IDENTIFIER")) {
|
|
890
|
-
throw new ParseError("Expected selector name after #", this.tokens.peek().position);
|
|
891
|
-
}
|
|
892
|
-
const selectorName = this.tokens.consume().value;
|
|
893
|
-
selector = `#${selectorName}`;
|
|
894
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
895
|
-
throw new ParseError("Expected ] after selector", this.tokens.peek().position);
|
|
896
|
-
}
|
|
897
|
-
this.tokens.consume();
|
|
898
|
-
if (this.tokens.match("COMMA")) {
|
|
899
|
-
this.tokens.consume();
|
|
900
|
-
if (!this.tokens.match("LBRACKET")) {
|
|
901
|
-
throw new ParseError("Expected [ after comma", this.tokens.peek().position);
|
|
902
|
-
}
|
|
903
|
-
this.tokens.consume();
|
|
904
|
-
if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
|
|
905
|
-
throw new ParseError("Expected column name", this.tokens.peek().position);
|
|
906
|
-
}
|
|
907
|
-
const colStart = this.parseColumnName();
|
|
908
|
-
if (this.tokens.match("COLON") && !this.tokens.peekAhead(1)?.type.match(/RBRACKET/)) {
|
|
909
|
-
this.tokens.consume();
|
|
910
|
-
if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
|
|
911
|
-
throw new ParseError("Expected end column name after :", this.tokens.peek().position);
|
|
912
|
-
}
|
|
913
|
-
const colEnd = this.parseColumnName();
|
|
914
|
-
cols = {
|
|
915
|
-
startCol: colStart,
|
|
916
|
-
endCol: colEnd
|
|
917
|
-
};
|
|
918
|
-
} else {
|
|
919
|
-
cols = {
|
|
920
|
-
startCol: colStart,
|
|
921
|
-
endCol: colStart
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
925
|
-
throw new ParseError("Expected ] after column specification", this.tokens.peek().position);
|
|
926
|
-
}
|
|
927
|
-
this.tokens.consume();
|
|
928
|
-
if (this.tokens.match("COLON")) {
|
|
929
|
-
this.tokens.consume();
|
|
930
|
-
if (!this.tokens.match("LBRACKET")) {
|
|
931
|
-
throw new ParseError("Expected [ after : in column range", this.tokens.peek().position);
|
|
932
|
-
}
|
|
933
|
-
this.tokens.consume();
|
|
934
|
-
if (!this.tokens.match("IDENTIFIER") && !this.tokens.match("FUNCTION") && !this.tokens.match("NUMBER") && !this.tokens.match("OPERATOR")) {
|
|
935
|
-
throw new ParseError("Expected end column name", this.tokens.peek().position);
|
|
936
|
-
}
|
|
937
|
-
const colEnd = this.parseColumnName();
|
|
938
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
939
|
-
throw new ParseError("Expected ] after end column name", this.tokens.peek().position);
|
|
940
|
-
}
|
|
941
|
-
this.tokens.consume();
|
|
942
|
-
cols = {
|
|
943
|
-
startCol: colStart,
|
|
944
|
-
endCol: colEnd
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
949
|
-
throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
|
|
950
|
-
}
|
|
951
|
-
this.tokens.consume();
|
|
952
|
-
} else if (this.tokens.match("IDENTIFIER") || this.tokens.match("FUNCTION") || this.tokens.match("NUMBER") || this.tokens.match("OPERATOR")) {
|
|
953
|
-
const colStart = this.parseColumnName();
|
|
954
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
955
|
-
throw new ParseError("Expected ] after column name", this.tokens.peek().position);
|
|
956
|
-
}
|
|
957
|
-
this.tokens.consume();
|
|
958
|
-
if (this.tokens.match("COLON")) {
|
|
959
|
-
this.tokens.consume();
|
|
960
|
-
if (!this.tokens.match("LBRACKET")) {
|
|
961
|
-
throw new ParseError("Expected [ before second column name", this.tokens.peek().position);
|
|
962
|
-
}
|
|
963
|
-
this.tokens.consume();
|
|
964
|
-
const colEnd = this.parseColumnName();
|
|
965
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
966
|
-
throw new ParseError("Expected ] after second column name", this.tokens.peek().position);
|
|
967
|
-
}
|
|
968
|
-
this.tokens.consume();
|
|
969
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
970
|
-
throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
|
|
971
|
-
}
|
|
972
|
-
this.tokens.consume();
|
|
973
|
-
cols = {
|
|
974
|
-
startCol: colStart,
|
|
975
|
-
endCol: colEnd
|
|
976
|
-
};
|
|
977
|
-
} else {
|
|
978
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
979
|
-
throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
|
|
980
|
-
}
|
|
981
|
-
this.tokens.consume();
|
|
982
|
-
cols = {
|
|
983
|
-
startCol: colStart,
|
|
984
|
-
endCol: colStart
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
} else if (this.tokens.match("AT")) {
|
|
989
|
-
this.tokens.consume();
|
|
990
|
-
isCurrentRow = true;
|
|
991
|
-
if (this.tokens.match("LBRACKET")) {
|
|
992
|
-
this.tokens.consume();
|
|
993
|
-
const colStart = this.parseColumnName();
|
|
994
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
995
|
-
throw new ParseError("Expected ] after column name", this.tokens.peek().position);
|
|
996
|
-
}
|
|
997
|
-
this.tokens.consume();
|
|
998
|
-
if (this.tokens.match("COLON")) {
|
|
999
|
-
this.tokens.consume();
|
|
1000
|
-
if (!this.tokens.match("LBRACKET")) {
|
|
1001
|
-
throw new ParseError("Expected [ before second column name", this.tokens.peek().position);
|
|
1002
|
-
}
|
|
1003
|
-
this.tokens.consume();
|
|
1004
|
-
const colEnd = this.parseColumnName();
|
|
1005
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
1006
|
-
throw new ParseError("Expected ] after second column name", this.tokens.peek().position);
|
|
1007
|
-
}
|
|
1008
|
-
this.tokens.consume();
|
|
1009
|
-
cols = {
|
|
1010
|
-
startCol: colStart,
|
|
1011
|
-
endCol: colEnd
|
|
1012
|
-
};
|
|
1013
|
-
} else {
|
|
1014
|
-
cols = {
|
|
1015
|
-
startCol: colStart,
|
|
1016
|
-
endCol: colStart
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
1020
|
-
throw new ParseError("Expected ] to close table reference", this.tokens.peek().position);
|
|
1021
|
-
}
|
|
1022
|
-
this.tokens.consume();
|
|
1023
|
-
} else {
|
|
1024
|
-
const colStart = this.parseColumnName();
|
|
1025
|
-
if (this.tokens.match("COLON")) {
|
|
1026
|
-
this.tokens.consume();
|
|
1027
|
-
const colEnd = this.parseColumnName();
|
|
1028
|
-
cols = {
|
|
1029
|
-
startCol: colStart,
|
|
1030
|
-
endCol: colEnd
|
|
1031
|
-
};
|
|
1032
|
-
} else {
|
|
1033
|
-
cols = {
|
|
1034
|
-
startCol: colStart,
|
|
1035
|
-
endCol: colStart
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
1039
|
-
throw new ParseError("Expected ] after column reference", this.tokens.peek().position);
|
|
1040
|
-
}
|
|
1041
|
-
this.tokens.consume();
|
|
1042
|
-
}
|
|
1043
|
-
} else if (this.tokens.match("IDENTIFIER") || this.tokens.match("FUNCTION") || this.tokens.match("NUMBER") || this.tokens.match("OPERATOR")) {
|
|
1044
|
-
const colStart = this.parseColumnName();
|
|
1045
|
-
if (this.tokens.match("COLON")) {
|
|
1046
|
-
this.tokens.consume();
|
|
1047
|
-
const colEnd = this.parseColumnName();
|
|
1048
|
-
cols = {
|
|
1049
|
-
startCol: colStart,
|
|
1050
|
-
endCol: colEnd
|
|
1051
|
-
};
|
|
1052
|
-
} else {
|
|
1053
|
-
cols = {
|
|
1054
|
-
startCol: colStart,
|
|
1055
|
-
endCol: colStart
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
1059
|
-
throw new ParseError("Expected ] after column name", this.tokens.peek().position);
|
|
1060
|
-
}
|
|
1061
|
-
this.tokens.consume();
|
|
1062
|
-
} else if (this.tokens.match("HASH")) {
|
|
1063
|
-
this.tokens.consume();
|
|
1064
|
-
if (!this.tokens.match("IDENTIFIER")) {
|
|
1065
|
-
throw new ParseError("Expected selector name after #", this.tokens.peek().position);
|
|
1066
|
-
}
|
|
1067
|
-
const selectorName = this.tokens.consume().value;
|
|
1068
|
-
selector = `#${selectorName}`;
|
|
1069
|
-
if (!this.tokens.match("RBRACKET")) {
|
|
1070
|
-
throw new ParseError("Expected ] after selector", this.tokens.peek().position);
|
|
1071
|
-
}
|
|
1072
|
-
this.tokens.consume();
|
|
1073
|
-
} else {
|
|
1074
|
-
throw new ParseError("Expected column name or selector in table reference", this.tokens.peek().position);
|
|
1075
|
-
}
|
|
1076
|
-
return createStructuredReferenceNode({
|
|
1077
|
-
tableName,
|
|
1078
|
-
sheetName,
|
|
1079
|
-
cols,
|
|
1080
|
-
selector,
|
|
1081
|
-
isCurrentRow,
|
|
1082
|
-
position: {
|
|
1083
|
-
start: startPos,
|
|
1084
|
-
end: this.tokens.peek().position?.end ?? 0
|
|
1085
|
-
}
|
|
1086
|
-
});
|
|
901
|
+
return this.parseTableReference(tableName, startPos, { sheetName });
|
|
1087
902
|
}
|
|
1088
903
|
parseCellOrRangeAfterSheets() {
|
|
1089
904
|
const start = this.tokens.peek().position?.start ?? 0;
|
|
@@ -1257,13 +1072,6 @@ class Parser {
|
|
|
1257
1072
|
rangeNode.workbookName = workbookName;
|
|
1258
1073
|
return rangeNode;
|
|
1259
1074
|
}
|
|
1260
|
-
parseTableReferenceWithWorkbookAndSheet(tableName, workbookName, sheetName, startPos) {
|
|
1261
|
-
const tableRef = this.parseTableReferenceWithSheet(tableName, sheetName, startPos);
|
|
1262
|
-
if (tableRef.type === "structured-reference") {
|
|
1263
|
-
tableRef.workbookName = workbookName;
|
|
1264
|
-
}
|
|
1265
|
-
return tableRef;
|
|
1266
|
-
}
|
|
1267
1075
|
parseRange(startRef, endRef, startPos, endPos) {
|
|
1268
1076
|
let fullRange = `${startRef}:${endRef}`;
|
|
1269
1077
|
let sheetName;
|
|
@@ -1575,7 +1383,7 @@ class Parser {
|
|
|
1575
1383
|
const identifier2 = this.tokens.consume();
|
|
1576
1384
|
ref += identifier2.value;
|
|
1577
1385
|
if (this.tokens.match("LBRACKET")) {
|
|
1578
|
-
|
|
1386
|
+
throw new ParseError(`Cross-workbook table references use [${workbookName}]!${identifier2.value}[...] instead of [${workbookName}]${sheetName}!${identifier2.value}[...]`, this.tokens.peek().position);
|
|
1579
1387
|
}
|
|
1580
1388
|
if (this.tokens.match("COLON")) {
|
|
1581
1389
|
this.tokens.consume();
|
|
@@ -1689,4 +1497,4 @@ export {
|
|
|
1689
1497
|
ParseError
|
|
1690
1498
|
};
|
|
1691
1499
|
|
|
1692
|
-
//# debugId=
|
|
1500
|
+
//# debugId=E7749E5263A4F26E64756E2164756E21
|