@bilig/formula 0.1.2 → 0.1.4
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/builtins/placeholder.d.ts +2 -2
- package/dist/builtins/text-format-builtins.d.ts +14 -0
- package/dist/builtins/text-format-builtins.js +537 -0
- package/dist/builtins/text-format-builtins.js.map +1 -0
- package/dist/builtins/text-search-builtins.d.ts +21 -0
- package/dist/builtins/text-search-builtins.js +469 -0
- package/dist/builtins/text-search-builtins.js.map +1 -0
- package/dist/builtins/text.js +31 -968
- package/dist/builtins/text.js.map +1 -1
- package/dist/js-evaluator-array-special-calls.d.ts +28 -0
- package/dist/js-evaluator-array-special-calls.js +340 -0
- package/dist/js-evaluator-array-special-calls.js.map +1 -0
- package/dist/js-evaluator-context-special-calls.d.ts +24 -0
- package/dist/js-evaluator-context-special-calls.js +156 -0
- package/dist/js-evaluator-context-special-calls.js.map +1 -0
- package/dist/js-evaluator-types.d.ts +117 -0
- package/dist/js-evaluator-types.js +2 -0
- package/dist/js-evaluator-types.js.map +1 -0
- package/dist/js-evaluator-workbook-special-calls.d.ts +24 -0
- package/dist/js-evaluator-workbook-special-calls.js +185 -0
- package/dist/js-evaluator-workbook-special-calls.js.map +1 -0
- package/dist/js-evaluator.d.ts +3 -100
- package/dist/js-evaluator.js +58 -672
- package/dist/js-evaluator.js.map +1 -1
- package/package.json +6 -6
package/dist/js-evaluator.js
CHANGED
|
@@ -2,7 +2,9 @@ import { ErrorCode, ValueTag, formatErrorCode } from "@bilig/protocol";
|
|
|
2
2
|
import { indexToColumn, parseCellAddress, parseRangeAddress } from "./addressing.js";
|
|
3
3
|
import { getBuiltin, hasBuiltin } from "./builtins.js";
|
|
4
4
|
import { getLookupBuiltin } from "./builtins/lookup.js";
|
|
5
|
-
import {
|
|
5
|
+
import { evaluateArraySpecialCall } from "./js-evaluator-array-special-calls.js";
|
|
6
|
+
import { evaluateContextSpecialCall } from "./js-evaluator-context-special-calls.js";
|
|
7
|
+
import { evaluateWorkbookSpecialCall } from "./js-evaluator-workbook-special-calls.js";
|
|
6
8
|
import { isArrayValue, scalarFromEvaluationResult, } from "./runtime-values.js";
|
|
7
9
|
import { rewriteSpecialCall } from "./special-call-rewrites.js";
|
|
8
10
|
function emptyValue() {
|
|
@@ -540,29 +542,6 @@ function coerceOptionalTrimModeArgument(value, fallback) {
|
|
|
540
542
|
function isCellValueError(value) {
|
|
541
543
|
return typeof value === "object" && value !== null && "tag" in value;
|
|
542
544
|
}
|
|
543
|
-
function indexOfWithMatchMode(text, delimiter, startIndex, matchMode) {
|
|
544
|
-
if (matchMode === 1) {
|
|
545
|
-
return text.toLowerCase().indexOf(delimiter.toLowerCase(), startIndex);
|
|
546
|
-
}
|
|
547
|
-
return text.indexOf(delimiter, startIndex);
|
|
548
|
-
}
|
|
549
|
-
function splitTextByDelimiter(text, delimiter, matchMode) {
|
|
550
|
-
if (delimiter === "") {
|
|
551
|
-
return [text];
|
|
552
|
-
}
|
|
553
|
-
const parts = [];
|
|
554
|
-
let cursor = 0;
|
|
555
|
-
while (cursor <= text.length) {
|
|
556
|
-
const found = indexOfWithMatchMode(text, delimiter, cursor, matchMode);
|
|
557
|
-
if (found === -1) {
|
|
558
|
-
parts.push(text.slice(cursor));
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
parts.push(text.slice(cursor, found));
|
|
562
|
-
cursor = found + delimiter.length;
|
|
563
|
-
}
|
|
564
|
-
return parts;
|
|
565
|
-
}
|
|
566
545
|
function makeArrayStack(rows, cols, values) {
|
|
567
546
|
return { kind: "array", rows, cols, values };
|
|
568
547
|
}
|
|
@@ -628,9 +607,6 @@ function aggregateRangeSubset(functionArg, subset, context, totalSet) {
|
|
|
628
607
|
const result = builtin(...subset);
|
|
629
608
|
return isArrayValue(result) ? scalarFromEvaluationResult(result) : result;
|
|
630
609
|
}
|
|
631
|
-
function isTrimRangeEmptyCell(value) {
|
|
632
|
-
return value.tag === ValueTag.Empty;
|
|
633
|
-
}
|
|
634
610
|
function applyLambda(lambdaValue, args, context) {
|
|
635
611
|
if (lambdaValue.kind !== "lambda") {
|
|
636
612
|
return stackScalar(lambdaValue.kind === "scalar" && lambdaValue.value.tag === ValueTag.Error
|
|
@@ -649,652 +625,62 @@ function applyLambda(lambdaValue, args, context) {
|
|
|
649
625
|
}
|
|
650
626
|
function evaluateSpecialCall(callee, rawArgs, context, argRefs = []) {
|
|
651
627
|
switch (callee) {
|
|
652
|
-
case "ROW": {
|
|
653
|
-
if (rawArgs.length > 1) {
|
|
654
|
-
return stackScalar(error(ErrorCode.Value));
|
|
655
|
-
}
|
|
656
|
-
const row = referenceRowNumber(argRefs[0], context);
|
|
657
|
-
return stackScalar(row === undefined ? error(ErrorCode.Value) : numberValue(row));
|
|
658
|
-
}
|
|
659
|
-
case "COLUMN": {
|
|
660
|
-
if (rawArgs.length > 1) {
|
|
661
|
-
return stackScalar(error(ErrorCode.Value));
|
|
662
|
-
}
|
|
663
|
-
const column = referenceColumnNumber(argRefs[0], context);
|
|
664
|
-
return stackScalar(column === undefined ? error(ErrorCode.Value) : numberValue(column));
|
|
665
|
-
}
|
|
666
|
-
case "ISOMITTED": {
|
|
667
|
-
if (rawArgs.length !== 1) {
|
|
668
|
-
return stackScalar(error(ErrorCode.Value));
|
|
669
|
-
}
|
|
670
|
-
return stackScalar({ tag: ValueTag.Boolean, value: rawArgs[0]?.kind === "omitted" });
|
|
671
|
-
}
|
|
672
|
-
case "FORMULATEXT": {
|
|
673
|
-
if (rawArgs.length !== 1) {
|
|
674
|
-
return stackScalar(error(ErrorCode.Value));
|
|
675
|
-
}
|
|
676
|
-
const address = referenceTopLeftAddress(argRefs[0]);
|
|
677
|
-
const sheetName = referenceSheetName(argRefs[0], context);
|
|
678
|
-
if (!address || !sheetName) {
|
|
679
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
680
|
-
}
|
|
681
|
-
const formula = context.resolveFormula?.(sheetName, address);
|
|
682
|
-
return stackScalar(formula
|
|
683
|
-
? stringValue(formula.startsWith("=") ? formula : `=${formula}`)
|
|
684
|
-
: error(ErrorCode.NA));
|
|
685
|
-
}
|
|
686
|
-
case "FORMULA": {
|
|
687
|
-
if (rawArgs.length !== 1) {
|
|
688
|
-
return stackScalar(error(ErrorCode.Value));
|
|
689
|
-
}
|
|
690
|
-
const address = referenceTopLeftAddress(argRefs[0]);
|
|
691
|
-
const sheetName = referenceSheetName(argRefs[0], context);
|
|
692
|
-
if (!address || !sheetName) {
|
|
693
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
694
|
-
}
|
|
695
|
-
const formula = context.resolveFormula?.(sheetName, address);
|
|
696
|
-
return stackScalar(formula
|
|
697
|
-
? stringValue(formula.startsWith("=") ? formula : `=${formula}`)
|
|
698
|
-
: error(ErrorCode.NA));
|
|
699
|
-
}
|
|
700
|
-
case "PHONETIC": {
|
|
701
|
-
if (rawArgs.length !== 1) {
|
|
702
|
-
return stackScalar(error(ErrorCode.Value));
|
|
703
|
-
}
|
|
704
|
-
const target = rawArgs[0];
|
|
705
|
-
if (target.kind === "scalar") {
|
|
706
|
-
return stackScalar(stringValue(toStringValue(target.value)));
|
|
707
|
-
}
|
|
708
|
-
if (target.kind === "range") {
|
|
709
|
-
return stackScalar(stringValue(toStringValue(target.values[0] ?? emptyValue())));
|
|
710
|
-
}
|
|
711
|
-
return stackScalar(error(ErrorCode.Value));
|
|
712
|
-
}
|
|
713
|
-
case "GETPIVOTDATA": {
|
|
714
|
-
if (rawArgs.length < 2 || (rawArgs.length - 2) % 2 !== 0) {
|
|
715
|
-
return stackScalar(error(ErrorCode.Value));
|
|
716
|
-
}
|
|
717
|
-
const dataFieldValue = isSingleCellValue(rawArgs[0]);
|
|
718
|
-
const address = referenceTopLeftAddress(argRefs[1]);
|
|
719
|
-
const sheetName = referenceSheetName(argRefs[1], context);
|
|
720
|
-
if (!dataFieldValue) {
|
|
721
|
-
return stackScalar(error(ErrorCode.Value));
|
|
722
|
-
}
|
|
723
|
-
if (!address || !sheetName) {
|
|
724
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
725
|
-
}
|
|
726
|
-
const filters = [];
|
|
727
|
-
for (let index = 2; index < rawArgs.length; index += 2) {
|
|
728
|
-
const fieldValue = isSingleCellValue(rawArgs[index]);
|
|
729
|
-
const itemValue = isSingleCellValue(rawArgs[index + 1]);
|
|
730
|
-
if (!fieldValue || !itemValue) {
|
|
731
|
-
return stackScalar(error(ErrorCode.Value));
|
|
732
|
-
}
|
|
733
|
-
filters.push({ field: toStringValue(fieldValue), item: itemValue });
|
|
734
|
-
}
|
|
735
|
-
return stackScalar(context.resolvePivotData?.({
|
|
736
|
-
dataField: toStringValue(dataFieldValue),
|
|
737
|
-
sheetName,
|
|
738
|
-
address,
|
|
739
|
-
filters,
|
|
740
|
-
}) ?? error(ErrorCode.Ref));
|
|
741
|
-
}
|
|
742
|
-
case "GROUPBY": {
|
|
743
|
-
if (rawArgs.length < 3 || rawArgs.length > 8) {
|
|
744
|
-
return stackScalar(error(ErrorCode.Value));
|
|
745
|
-
}
|
|
746
|
-
const rowFields = matrixFromStackValue(rawArgs[0]);
|
|
747
|
-
const values = matrixFromStackValue(rawArgs[1]);
|
|
748
|
-
if (!rowFields || !values) {
|
|
749
|
-
return stackScalar(error(ErrorCode.Value));
|
|
750
|
-
}
|
|
751
|
-
const sortOrder = vectorIntegerArgument(rawArgs[5]) ??
|
|
752
|
-
(rawArgs[5] ? [scalarIntegerArgument(rawArgs[5]) ?? Number.NaN] : undefined);
|
|
753
|
-
const fieldHeadersMode = scalarIntegerArgument(rawArgs[3]);
|
|
754
|
-
const totalDepth = scalarIntegerArgument(rawArgs[4]);
|
|
755
|
-
const filterArray = rawArgs[6] ? matrixFromStackValue(rawArgs[6]) : undefined;
|
|
756
|
-
const fieldRelationship = scalarIntegerArgument(rawArgs[7]);
|
|
757
|
-
const groupByOptions = {
|
|
758
|
-
aggregate: (subset, totalSet) => aggregateRangeSubset(rawArgs[2], subset, context, totalSet),
|
|
759
|
-
...(fieldHeadersMode !== undefined ? { fieldHeadersMode } : {}),
|
|
760
|
-
...(totalDepth !== undefined ? { totalDepth } : {}),
|
|
761
|
-
...(sortOrder?.every(Number.isFinite) ? { sortOrder } : {}),
|
|
762
|
-
...(filterArray !== undefined ? { filterArray } : {}),
|
|
763
|
-
...(fieldRelationship !== undefined ? { fieldRelationship } : {}),
|
|
764
|
-
};
|
|
765
|
-
const result = evaluateGroupBy(rowFields, values, groupByOptions);
|
|
766
|
-
return isArrayValue(result) ? result : stackScalar(result);
|
|
767
|
-
}
|
|
768
|
-
case "PIVOTBY": {
|
|
769
|
-
if (rawArgs.length < 4 || rawArgs.length > 11) {
|
|
770
|
-
return stackScalar(error(ErrorCode.Value));
|
|
771
|
-
}
|
|
772
|
-
const rowFields = matrixFromStackValue(rawArgs[0]);
|
|
773
|
-
const colFields = matrixFromStackValue(rawArgs[1]);
|
|
774
|
-
const values = matrixFromStackValue(rawArgs[2]);
|
|
775
|
-
if (!rowFields || !colFields || !values) {
|
|
776
|
-
return stackScalar(error(ErrorCode.Value));
|
|
777
|
-
}
|
|
778
|
-
const rowSortOrder = vectorIntegerArgument(rawArgs[6]) ??
|
|
779
|
-
(rawArgs[6] ? [scalarIntegerArgument(rawArgs[6]) ?? Number.NaN] : undefined);
|
|
780
|
-
const colSortOrder = vectorIntegerArgument(rawArgs[8]) ??
|
|
781
|
-
(rawArgs[8] ? [scalarIntegerArgument(rawArgs[8]) ?? Number.NaN] : undefined);
|
|
782
|
-
const fieldHeadersMode = scalarIntegerArgument(rawArgs[4]);
|
|
783
|
-
const rowTotalDepth = scalarIntegerArgument(rawArgs[5]);
|
|
784
|
-
const colTotalDepth = scalarIntegerArgument(rawArgs[7]);
|
|
785
|
-
const filterArray = rawArgs[9] ? matrixFromStackValue(rawArgs[9]) : undefined;
|
|
786
|
-
const relativeTo = scalarIntegerArgument(rawArgs[10]);
|
|
787
|
-
const pivotByOptions = {
|
|
788
|
-
aggregate: (subset, totalSet) => aggregateRangeSubset(rawArgs[3], subset, context, totalSet),
|
|
789
|
-
...(fieldHeadersMode !== undefined ? { fieldHeadersMode } : {}),
|
|
790
|
-
...(rowTotalDepth !== undefined ? { rowTotalDepth } : {}),
|
|
791
|
-
...(rowSortOrder?.every(Number.isFinite) ? { rowSortOrder } : {}),
|
|
792
|
-
...(colTotalDepth !== undefined ? { colTotalDepth } : {}),
|
|
793
|
-
...(colSortOrder?.every(Number.isFinite) ? { colSortOrder } : {}),
|
|
794
|
-
...(filterArray !== undefined ? { filterArray } : {}),
|
|
795
|
-
...(relativeTo !== undefined ? { relativeTo } : {}),
|
|
796
|
-
};
|
|
797
|
-
const result = evaluatePivotBy(rowFields, colFields, values, pivotByOptions);
|
|
798
|
-
return isArrayValue(result) ? result : stackScalar(result);
|
|
799
|
-
}
|
|
800
|
-
case "MULTIPLE.OPERATIONS": {
|
|
801
|
-
if (rawArgs.length !== 3 && rawArgs.length !== 5) {
|
|
802
|
-
return stackScalar(error(ErrorCode.Value));
|
|
803
|
-
}
|
|
804
|
-
const formulaAddress = referenceTopLeftAddress(argRefs[0]);
|
|
805
|
-
const formulaSheetName = referenceSheetName(argRefs[0], context);
|
|
806
|
-
const rowCellAddress = referenceTopLeftAddress(argRefs[1]);
|
|
807
|
-
const rowCellSheetName = referenceSheetName(argRefs[1], context);
|
|
808
|
-
const rowReplacementAddress = referenceTopLeftAddress(argRefs[2]);
|
|
809
|
-
const rowReplacementSheetName = referenceSheetName(argRefs[2], context);
|
|
810
|
-
if (!formulaAddress ||
|
|
811
|
-
!formulaSheetName ||
|
|
812
|
-
!rowCellAddress ||
|
|
813
|
-
!rowCellSheetName ||
|
|
814
|
-
!rowReplacementAddress ||
|
|
815
|
-
!rowReplacementSheetName) {
|
|
816
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
817
|
-
}
|
|
818
|
-
const columnCellAddress = rawArgs.length === 5 ? referenceTopLeftAddress(argRefs[3]) : undefined;
|
|
819
|
-
const columnCellSheetName = rawArgs.length === 5 ? referenceSheetName(argRefs[3], context) : undefined;
|
|
820
|
-
const columnReplacementAddress = rawArgs.length === 5 ? referenceTopLeftAddress(argRefs[4]) : undefined;
|
|
821
|
-
const columnReplacementSheetName = rawArgs.length === 5 ? referenceSheetName(argRefs[4], context) : undefined;
|
|
822
|
-
if (rawArgs.length === 5 &&
|
|
823
|
-
(!columnCellAddress ||
|
|
824
|
-
!columnCellSheetName ||
|
|
825
|
-
!columnReplacementAddress ||
|
|
826
|
-
!columnReplacementSheetName)) {
|
|
827
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
828
|
-
}
|
|
829
|
-
const request = {
|
|
830
|
-
formulaSheetName,
|
|
831
|
-
formulaAddress,
|
|
832
|
-
rowCellSheetName,
|
|
833
|
-
rowCellAddress,
|
|
834
|
-
rowReplacementSheetName,
|
|
835
|
-
rowReplacementAddress,
|
|
836
|
-
...(columnCellSheetName ? { columnCellSheetName } : {}),
|
|
837
|
-
...(columnCellAddress ? { columnCellAddress } : {}),
|
|
838
|
-
...(columnReplacementSheetName ? { columnReplacementSheetName } : {}),
|
|
839
|
-
...(columnReplacementAddress ? { columnReplacementAddress } : {}),
|
|
840
|
-
};
|
|
841
|
-
return stackScalar(context.resolveMultipleOperations?.(request) ?? error(ErrorCode.Ref));
|
|
842
|
-
}
|
|
843
|
-
case "CHOOSE": {
|
|
844
|
-
if (rawArgs.length < 2) {
|
|
845
|
-
return stackScalar(error(ErrorCode.Value));
|
|
846
|
-
}
|
|
847
|
-
const indexValue = isSingleCellValue(rawArgs[0]);
|
|
848
|
-
const choice = indexValue ? toNumber(indexValue) : undefined;
|
|
849
|
-
if (choice === undefined || !Number.isFinite(choice)) {
|
|
850
|
-
return stackScalar(error(ErrorCode.Value));
|
|
851
|
-
}
|
|
852
|
-
const truncated = Math.trunc(choice);
|
|
853
|
-
if (truncated < 1 || truncated >= rawArgs.length) {
|
|
854
|
-
return stackScalar(error(ErrorCode.Value));
|
|
855
|
-
}
|
|
856
|
-
return cloneStackValue(rawArgs[truncated]);
|
|
857
|
-
}
|
|
858
|
-
case "SHEET": {
|
|
859
|
-
if (rawArgs.length > 1) {
|
|
860
|
-
return stackScalar(error(ErrorCode.Value));
|
|
861
|
-
}
|
|
862
|
-
if (rawArgs.length === 0) {
|
|
863
|
-
const index = sheetIndexByName(context.sheetName, context);
|
|
864
|
-
return stackScalar(index === undefined ? error(ErrorCode.NA) : numberValue(index));
|
|
865
|
-
}
|
|
866
|
-
if (argRefs[0]) {
|
|
867
|
-
const index = sheetIndexByName(referenceSheetName(argRefs[0], context) ?? context.sheetName, context);
|
|
868
|
-
return stackScalar(index === undefined ? error(ErrorCode.NA) : numberValue(index));
|
|
869
|
-
}
|
|
870
|
-
const scalar = isSingleCellValue(rawArgs[0]);
|
|
871
|
-
if (scalar?.tag !== ValueTag.String) {
|
|
872
|
-
return stackScalar(error(ErrorCode.NA));
|
|
873
|
-
}
|
|
874
|
-
const index = sheetIndexByName(scalar.value, context);
|
|
875
|
-
return stackScalar(index === undefined ? error(ErrorCode.NA) : numberValue(index));
|
|
876
|
-
}
|
|
877
|
-
case "SHEETS": {
|
|
878
|
-
if (rawArgs.length > 1) {
|
|
879
|
-
return stackScalar(error(ErrorCode.Value));
|
|
880
|
-
}
|
|
881
|
-
if (rawArgs.length === 0) {
|
|
882
|
-
return stackScalar(numberValue(sheetNames(context).length));
|
|
883
|
-
}
|
|
884
|
-
if (argRefs[0]) {
|
|
885
|
-
return stackScalar(numberValue(1));
|
|
886
|
-
}
|
|
887
|
-
const scalar = isSingleCellValue(rawArgs[0]);
|
|
888
|
-
if (scalar?.tag !== ValueTag.String) {
|
|
889
|
-
return stackScalar(error(ErrorCode.NA));
|
|
890
|
-
}
|
|
891
|
-
return stackScalar(sheetIndexByName(scalar.value, context) === undefined
|
|
892
|
-
? error(ErrorCode.NA)
|
|
893
|
-
: numberValue(1));
|
|
894
|
-
}
|
|
895
|
-
case "CELL": {
|
|
896
|
-
if (rawArgs.length < 1 || rawArgs.length > 2) {
|
|
897
|
-
return stackScalar(error(ErrorCode.Value));
|
|
898
|
-
}
|
|
899
|
-
const infoType = isSingleCellValue(rawArgs[0]);
|
|
900
|
-
if (infoType?.tag !== ValueTag.String) {
|
|
901
|
-
return stackScalar(error(ErrorCode.Value));
|
|
902
|
-
}
|
|
903
|
-
const ref = rawArgs.length === 2 ? argRefs[1] : currentCellReference(context);
|
|
904
|
-
if (!ref) {
|
|
905
|
-
return stackScalar(error(ErrorCode.Value));
|
|
906
|
-
}
|
|
907
|
-
const normalizedInfoType = infoType.value.trim().toLowerCase();
|
|
908
|
-
switch (normalizedInfoType) {
|
|
909
|
-
case "address": {
|
|
910
|
-
const address = absoluteAddress(ref, context);
|
|
911
|
-
return stackScalar(address ? stringValue(address) : error(ErrorCode.Value));
|
|
912
|
-
}
|
|
913
|
-
case "row": {
|
|
914
|
-
const row = referenceRowNumber(ref, context);
|
|
915
|
-
return stackScalar(row === undefined ? error(ErrorCode.Value) : numberValue(row));
|
|
916
|
-
}
|
|
917
|
-
case "col": {
|
|
918
|
-
const column = referenceColumnNumber(ref, context);
|
|
919
|
-
return stackScalar(column === undefined ? error(ErrorCode.Value) : numberValue(column));
|
|
920
|
-
}
|
|
921
|
-
case "contents": {
|
|
922
|
-
const address = referenceTopLeftAddress(ref);
|
|
923
|
-
const sheetName = referenceSheetName(ref, context);
|
|
924
|
-
if (!address || !sheetName) {
|
|
925
|
-
return stackScalar(error(ErrorCode.Value));
|
|
926
|
-
}
|
|
927
|
-
return stackScalar(context.resolveCell(sheetName, address));
|
|
928
|
-
}
|
|
929
|
-
case "type": {
|
|
930
|
-
const address = referenceTopLeftAddress(ref);
|
|
931
|
-
const sheetName = referenceSheetName(ref, context);
|
|
932
|
-
if (!address || !sheetName) {
|
|
933
|
-
return stackScalar(error(ErrorCode.Value));
|
|
934
|
-
}
|
|
935
|
-
return stackScalar(stringValue(cellTypeCode(context.resolveCell(sheetName, address))));
|
|
936
|
-
}
|
|
937
|
-
case "filename":
|
|
938
|
-
return stackScalar(stringValue(""));
|
|
939
|
-
default:
|
|
940
|
-
return stackScalar(error(ErrorCode.Value));
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
case "INDIRECT": {
|
|
944
|
-
if (rawArgs.length < 1 || rawArgs.length > 2) {
|
|
945
|
-
return stackScalar(error(ErrorCode.Value));
|
|
946
|
-
}
|
|
947
|
-
const refText = coerceScalarTextArgument(rawArgs[0]);
|
|
948
|
-
if (isCellValueError(refText)) {
|
|
949
|
-
return stackScalar(refText);
|
|
950
|
-
}
|
|
951
|
-
const a1Mode = coerceOptionalBooleanArgument(rawArgs[1], true);
|
|
952
|
-
if (isCellValueError(a1Mode)) {
|
|
953
|
-
return stackScalar(a1Mode);
|
|
954
|
-
}
|
|
955
|
-
if (!a1Mode) {
|
|
956
|
-
return stackScalar(error(ErrorCode.Value));
|
|
957
|
-
}
|
|
958
|
-
const normalizedRefText = refText.trim();
|
|
959
|
-
if (normalizedRefText === "") {
|
|
960
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
961
|
-
}
|
|
962
|
-
try {
|
|
963
|
-
const cell = parseCellAddress(normalizedRefText, context.sheetName);
|
|
964
|
-
return stackScalar(context.resolveCell(cell.sheetName ?? context.sheetName, cell.text));
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
// fall through to range/name resolution
|
|
968
|
-
}
|
|
969
|
-
try {
|
|
970
|
-
const range = parseRangeAddress(normalizedRefText, context.sheetName);
|
|
971
|
-
if (range.kind !== "cells") {
|
|
972
|
-
return stackScalar(error(ErrorCode.Ref));
|
|
973
|
-
}
|
|
974
|
-
const targetSheetName = range.sheetName ?? context.sheetName;
|
|
975
|
-
const values = context.resolveRange(targetSheetName, range.start.text, range.end.text, "cells");
|
|
976
|
-
const rows = range.end.row - range.start.row + 1;
|
|
977
|
-
const cols = range.end.col - range.start.col + 1;
|
|
978
|
-
return {
|
|
979
|
-
kind: "range",
|
|
980
|
-
values,
|
|
981
|
-
refKind: "cells",
|
|
982
|
-
rows,
|
|
983
|
-
cols,
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
catch {
|
|
987
|
-
// fall through to name resolution
|
|
988
|
-
}
|
|
989
|
-
const resolvedName = context.resolveName?.(normalizedRefText);
|
|
990
|
-
return stackScalar(resolvedName ?? error(ErrorCode.Ref));
|
|
991
|
-
}
|
|
992
|
-
case "EXPAND": {
|
|
993
|
-
if (rawArgs.length < 2 || rawArgs.length > 4) {
|
|
994
|
-
return stackScalar(error(ErrorCode.Value));
|
|
995
|
-
}
|
|
996
|
-
const source = toRangeLike(rawArgs[0]);
|
|
997
|
-
const rows = coerceOptionalPositiveIntegerArgument(rawArgs[1], source.rows);
|
|
998
|
-
const cols = coerceOptionalPositiveIntegerArgument(rawArgs[2], source.cols);
|
|
999
|
-
if (isCellValueError(rows)) {
|
|
1000
|
-
return stackScalar(rows);
|
|
1001
|
-
}
|
|
1002
|
-
if (isCellValueError(cols)) {
|
|
1003
|
-
return stackScalar(cols);
|
|
1004
|
-
}
|
|
1005
|
-
const padArgument = rawArgs[3];
|
|
1006
|
-
const padValue = padArgument === undefined
|
|
1007
|
-
? error(ErrorCode.NA)
|
|
1008
|
-
: (() => {
|
|
1009
|
-
const scalar = isSingleCellValue(padArgument);
|
|
1010
|
-
return scalar ?? error(ErrorCode.Value);
|
|
1011
|
-
})();
|
|
1012
|
-
if (padValue.tag === ValueTag.Error && padArgument !== undefined) {
|
|
1013
|
-
const scalar = isSingleCellValue(padArgument);
|
|
1014
|
-
if (!scalar) {
|
|
1015
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
if (rows < source.rows || cols < source.cols) {
|
|
1019
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1020
|
-
}
|
|
1021
|
-
const values = [];
|
|
1022
|
-
for (let row = 0; row < rows; row += 1) {
|
|
1023
|
-
for (let col = 0; col < cols; col += 1) {
|
|
1024
|
-
values.push(row < source.rows && col < source.cols ? getRangeCell(source, row, col) : padValue);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
return makeArrayStack(rows, cols, values);
|
|
1028
|
-
}
|
|
1029
|
-
case "TEXTSPLIT": {
|
|
1030
|
-
if (rawArgs.length < 2 || rawArgs.length > 6) {
|
|
1031
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1032
|
-
}
|
|
1033
|
-
const text = coerceScalarTextArgument(rawArgs[0]);
|
|
1034
|
-
const columnDelimiter = coerceScalarTextArgument(rawArgs[1]);
|
|
1035
|
-
const rowDelimiter = rawArgs[2] === undefined ? undefined : coerceScalarTextArgument(rawArgs[2]);
|
|
1036
|
-
const ignoreEmpty = coerceOptionalBooleanArgument(rawArgs[3], false);
|
|
1037
|
-
const matchMode = coerceOptionalMatchModeArgument(rawArgs[4], 0);
|
|
1038
|
-
if (isCellValueError(text)) {
|
|
1039
|
-
return stackScalar(text);
|
|
1040
|
-
}
|
|
1041
|
-
if (isCellValueError(columnDelimiter)) {
|
|
1042
|
-
return stackScalar(columnDelimiter);
|
|
1043
|
-
}
|
|
1044
|
-
if (rowDelimiter !== undefined && isCellValueError(rowDelimiter)) {
|
|
1045
|
-
return stackScalar(rowDelimiter);
|
|
1046
|
-
}
|
|
1047
|
-
if (isCellValueError(ignoreEmpty)) {
|
|
1048
|
-
return stackScalar(ignoreEmpty);
|
|
1049
|
-
}
|
|
1050
|
-
if (isCellValueError(matchMode)) {
|
|
1051
|
-
return stackScalar(matchMode);
|
|
1052
|
-
}
|
|
1053
|
-
if (columnDelimiter === "" && rowDelimiter === undefined) {
|
|
1054
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1055
|
-
}
|
|
1056
|
-
const padArgument = rawArgs[5];
|
|
1057
|
-
const padValue = padArgument === undefined
|
|
1058
|
-
? error(ErrorCode.NA)
|
|
1059
|
-
: (() => {
|
|
1060
|
-
const scalar = isSingleCellValue(padArgument);
|
|
1061
|
-
return scalar ?? error(ErrorCode.Value);
|
|
1062
|
-
})();
|
|
1063
|
-
if (padArgument !== undefined && !isSingleCellValue(padArgument)) {
|
|
1064
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1065
|
-
}
|
|
1066
|
-
const rowSlices = rowDelimiter === undefined || rowDelimiter === ""
|
|
1067
|
-
? [text]
|
|
1068
|
-
: splitTextByDelimiter(text, rowDelimiter, matchMode);
|
|
1069
|
-
const matrix = rowSlices.map((rowSlice) => {
|
|
1070
|
-
const parts = columnDelimiter === ""
|
|
1071
|
-
? [rowSlice]
|
|
1072
|
-
: splitTextByDelimiter(rowSlice, columnDelimiter, matchMode);
|
|
1073
|
-
const filtered = ignoreEmpty ? parts.filter((part) => part !== "") : parts;
|
|
1074
|
-
return filtered.length === 0 ? [] : filtered;
|
|
1075
|
-
});
|
|
1076
|
-
const rows = Math.max(matrix.length, 1);
|
|
1077
|
-
const cols = Math.max(1, ...matrix.map((row) => row.length));
|
|
1078
|
-
const values = [];
|
|
1079
|
-
for (let rowIndex = 0; rowIndex < rows; rowIndex += 1) {
|
|
1080
|
-
const row = matrix[rowIndex] ?? [];
|
|
1081
|
-
for (let colIndex = 0; colIndex < cols; colIndex += 1) {
|
|
1082
|
-
values.push(colIndex < row.length ? stringValue(row[colIndex]) : padValue);
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
return makeArrayStack(rows, cols, values);
|
|
1086
|
-
}
|
|
1087
|
-
case "TRIMRANGE": {
|
|
1088
|
-
if (rawArgs.length < 1 || rawArgs.length > 3) {
|
|
1089
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1090
|
-
}
|
|
1091
|
-
const source = toRangeLike(rawArgs[0]);
|
|
1092
|
-
const trimRows = coerceOptionalTrimModeArgument(rawArgs[1], 3);
|
|
1093
|
-
const trimCols = coerceOptionalTrimModeArgument(rawArgs[2], 3);
|
|
1094
|
-
if (isCellValueError(trimRows)) {
|
|
1095
|
-
return stackScalar(trimRows);
|
|
1096
|
-
}
|
|
1097
|
-
if (isCellValueError(trimCols)) {
|
|
1098
|
-
return stackScalar(trimCols);
|
|
1099
|
-
}
|
|
1100
|
-
let startRow = 0;
|
|
1101
|
-
let endRow = source.rows - 1;
|
|
1102
|
-
let startCol = 0;
|
|
1103
|
-
let endCol = source.cols - 1;
|
|
1104
|
-
const trimLeadingRows = trimRows === 1 || trimRows === 3;
|
|
1105
|
-
const trimTrailingRows = trimRows === 2 || trimRows === 3;
|
|
1106
|
-
const trimLeadingCols = trimCols === 1 || trimCols === 3;
|
|
1107
|
-
const trimTrailingCols = trimCols === 2 || trimCols === 3;
|
|
1108
|
-
if (trimLeadingRows) {
|
|
1109
|
-
while (startRow <= endRow) {
|
|
1110
|
-
let hasNonEmpty = false;
|
|
1111
|
-
for (let col = 0; col < source.cols; col += 1) {
|
|
1112
|
-
if (!isTrimRangeEmptyCell(getRangeCell(source, startRow, col))) {
|
|
1113
|
-
hasNonEmpty = true;
|
|
1114
|
-
break;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (hasNonEmpty) {
|
|
1118
|
-
break;
|
|
1119
|
-
}
|
|
1120
|
-
startRow += 1;
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
if (trimTrailingRows) {
|
|
1124
|
-
while (endRow >= startRow) {
|
|
1125
|
-
let hasNonEmpty = false;
|
|
1126
|
-
for (let col = 0; col < source.cols; col += 1) {
|
|
1127
|
-
if (!isTrimRangeEmptyCell(getRangeCell(source, endRow, col))) {
|
|
1128
|
-
hasNonEmpty = true;
|
|
1129
|
-
break;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
if (hasNonEmpty) {
|
|
1133
|
-
break;
|
|
1134
|
-
}
|
|
1135
|
-
endRow -= 1;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
if (startRow > endRow) {
|
|
1139
|
-
return makeArrayStack(1, 1, [emptyValue()]);
|
|
1140
|
-
}
|
|
1141
|
-
if (trimLeadingCols) {
|
|
1142
|
-
while (startCol <= endCol) {
|
|
1143
|
-
let hasNonEmpty = false;
|
|
1144
|
-
for (let row = startRow; row <= endRow; row += 1) {
|
|
1145
|
-
if (!isTrimRangeEmptyCell(getRangeCell(source, row, startCol))) {
|
|
1146
|
-
hasNonEmpty = true;
|
|
1147
|
-
break;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
if (hasNonEmpty) {
|
|
1151
|
-
break;
|
|
1152
|
-
}
|
|
1153
|
-
startCol += 1;
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
if (trimTrailingCols) {
|
|
1157
|
-
while (endCol >= startCol) {
|
|
1158
|
-
let hasNonEmpty = false;
|
|
1159
|
-
for (let row = startRow; row <= endRow; row += 1) {
|
|
1160
|
-
if (!isTrimRangeEmptyCell(getRangeCell(source, row, endCol))) {
|
|
1161
|
-
hasNonEmpty = true;
|
|
1162
|
-
break;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
if (hasNonEmpty) {
|
|
1166
|
-
break;
|
|
1167
|
-
}
|
|
1168
|
-
endCol -= 1;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
if (startCol > endCol) {
|
|
1172
|
-
return makeArrayStack(1, 1, [emptyValue()]);
|
|
1173
|
-
}
|
|
1174
|
-
const rows = endRow - startRow + 1;
|
|
1175
|
-
const cols = endCol - startCol + 1;
|
|
1176
|
-
const values = [];
|
|
1177
|
-
for (let row = startRow; row <= endRow; row += 1) {
|
|
1178
|
-
for (let col = startCol; col <= endCol; col += 1) {
|
|
1179
|
-
values.push(getRangeCell(source, row, col));
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
return makeArrayStack(rows, cols, values);
|
|
1183
|
-
}
|
|
1184
|
-
case "MAKEARRAY": {
|
|
1185
|
-
if (rawArgs.length !== 3) {
|
|
1186
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1187
|
-
}
|
|
1188
|
-
const rows = toPositiveInteger(rawArgs[0]);
|
|
1189
|
-
const cols = toPositiveInteger(rawArgs[1]);
|
|
1190
|
-
if (rows === undefined || cols === undefined) {
|
|
1191
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1192
|
-
}
|
|
1193
|
-
const lambda = rawArgs[2];
|
|
1194
|
-
const values = [];
|
|
1195
|
-
for (let row = 1; row <= rows; row += 1) {
|
|
1196
|
-
for (let col = 1; col <= cols; col += 1) {
|
|
1197
|
-
const result = applyLambda(lambda, [
|
|
1198
|
-
stackScalar({ tag: ValueTag.Number, value: row }),
|
|
1199
|
-
stackScalar({ tag: ValueTag.Number, value: col }),
|
|
1200
|
-
], context);
|
|
1201
|
-
const scalar = isSingleCellValue(result);
|
|
1202
|
-
if (!scalar) {
|
|
1203
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1204
|
-
}
|
|
1205
|
-
values.push(scalar);
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
return { kind: "array", rows, cols, values };
|
|
1209
|
-
}
|
|
1210
|
-
case "MAP": {
|
|
1211
|
-
if (rawArgs.length < 2) {
|
|
1212
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1213
|
-
}
|
|
1214
|
-
const lambda = rawArgs[rawArgs.length - 1];
|
|
1215
|
-
const inputs = rawArgs.slice(0, -1);
|
|
1216
|
-
const shape = getBroadcastShape(inputs);
|
|
1217
|
-
if (!shape) {
|
|
1218
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1219
|
-
}
|
|
1220
|
-
const ranges = inputs.map(toRangeLike);
|
|
1221
|
-
const values = [];
|
|
1222
|
-
for (let row = 0; row < shape.rows; row += 1) {
|
|
1223
|
-
for (let col = 0; col < shape.cols; col += 1) {
|
|
1224
|
-
const lambdaArgs = ranges.map((range) => stackScalar(getRangeCell(range, Math.min(row, range.rows - 1), Math.min(col, range.cols - 1))));
|
|
1225
|
-
const result = applyLambda(lambda, lambdaArgs, context);
|
|
1226
|
-
const scalar = isSingleCellValue(result);
|
|
1227
|
-
if (!scalar) {
|
|
1228
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1229
|
-
}
|
|
1230
|
-
values.push(scalar);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return { kind: "array", rows: shape.rows, cols: shape.cols, values };
|
|
1234
|
-
}
|
|
1235
|
-
case "BYROW":
|
|
1236
|
-
case "BYCOL": {
|
|
1237
|
-
if (rawArgs.length !== 2) {
|
|
1238
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1239
|
-
}
|
|
1240
|
-
const source = toRangeLike(rawArgs[0]);
|
|
1241
|
-
const lambda = rawArgs[1];
|
|
1242
|
-
const values = [];
|
|
1243
|
-
if (callee === "BYROW") {
|
|
1244
|
-
for (let row = 0; row < source.rows; row += 1) {
|
|
1245
|
-
const rowValues = [];
|
|
1246
|
-
for (let col = 0; col < source.cols; col += 1) {
|
|
1247
|
-
rowValues.push(getRangeCell(source, row, col));
|
|
1248
|
-
}
|
|
1249
|
-
const result = applyLambda(lambda, [{ kind: "range", values: rowValues, rows: 1, cols: source.cols, refKind: "cells" }], context);
|
|
1250
|
-
const scalar = isSingleCellValue(result);
|
|
1251
|
-
if (!scalar) {
|
|
1252
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1253
|
-
}
|
|
1254
|
-
values.push(scalar);
|
|
1255
|
-
}
|
|
1256
|
-
return { kind: "array", rows: source.rows, cols: 1, values };
|
|
1257
|
-
}
|
|
1258
|
-
for (let col = 0; col < source.cols; col += 1) {
|
|
1259
|
-
const colValues = [];
|
|
1260
|
-
for (let row = 0; row < source.rows; row += 1) {
|
|
1261
|
-
colValues.push(getRangeCell(source, row, col));
|
|
1262
|
-
}
|
|
1263
|
-
const result = applyLambda(lambda, [{ kind: "range", values: colValues, rows: source.rows, cols: 1, refKind: "cells" }], context);
|
|
1264
|
-
const scalar = isSingleCellValue(result);
|
|
1265
|
-
if (!scalar) {
|
|
1266
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1267
|
-
}
|
|
1268
|
-
values.push(scalar);
|
|
1269
|
-
}
|
|
1270
|
-
return { kind: "array", rows: 1, cols: source.cols, values };
|
|
1271
|
-
}
|
|
1272
|
-
case "REDUCE":
|
|
1273
|
-
case "SCAN": {
|
|
1274
|
-
if (rawArgs.length !== 2 && rawArgs.length !== 3) {
|
|
1275
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1276
|
-
}
|
|
1277
|
-
const hasInitial = rawArgs.length === 3;
|
|
1278
|
-
let accumulator = hasInitial ? cloneStackValue(rawArgs[0]) : stackScalar(emptyValue());
|
|
1279
|
-
const source = toRangeLike(rawArgs[hasInitial ? 1 : 0]);
|
|
1280
|
-
const lambda = rawArgs[hasInitial ? 2 : 1];
|
|
1281
|
-
const scanValues = [];
|
|
1282
|
-
for (const cell of source.values) {
|
|
1283
|
-
accumulator = applyLambda(lambda, [accumulator, stackScalar(cell)], context);
|
|
1284
|
-
if (callee === "SCAN") {
|
|
1285
|
-
const scalar = isSingleCellValue(accumulator);
|
|
1286
|
-
if (!scalar) {
|
|
1287
|
-
return stackScalar(error(ErrorCode.Value));
|
|
1288
|
-
}
|
|
1289
|
-
scanValues.push(scalar);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
return callee === "SCAN"
|
|
1293
|
-
? { kind: "array", rows: source.rows, cols: source.cols, values: scanValues }
|
|
1294
|
-
: accumulator;
|
|
1295
|
-
}
|
|
1296
628
|
default:
|
|
1297
|
-
return
|
|
629
|
+
return (evaluateWorkbookSpecialCall(callee, rawArgs, context, argRefs, {
|
|
630
|
+
error,
|
|
631
|
+
stackScalar,
|
|
632
|
+
toStringValue,
|
|
633
|
+
isSingleCellValue,
|
|
634
|
+
matrixFromStackValue,
|
|
635
|
+
scalarIntegerArgument,
|
|
636
|
+
vectorIntegerArgument,
|
|
637
|
+
aggregateRangeSubset,
|
|
638
|
+
referenceTopLeftAddress,
|
|
639
|
+
referenceSheetName,
|
|
640
|
+
coerceScalarTextArgument,
|
|
641
|
+
coerceOptionalBooleanArgument,
|
|
642
|
+
isCellValueError,
|
|
643
|
+
}) ??
|
|
644
|
+
evaluateContextSpecialCall(callee, rawArgs, context, argRefs, {
|
|
645
|
+
error,
|
|
646
|
+
emptyValue,
|
|
647
|
+
numberValue,
|
|
648
|
+
stringValue,
|
|
649
|
+
stackScalar,
|
|
650
|
+
cloneStackValue,
|
|
651
|
+
toNumber,
|
|
652
|
+
toStringValue,
|
|
653
|
+
isSingleCellValue,
|
|
654
|
+
currentCellReference,
|
|
655
|
+
referenceSheetName,
|
|
656
|
+
referenceTopLeftAddress,
|
|
657
|
+
referenceRowNumber,
|
|
658
|
+
referenceColumnNumber,
|
|
659
|
+
absoluteAddress,
|
|
660
|
+
cellTypeCode,
|
|
661
|
+
sheetNames,
|
|
662
|
+
sheetIndexByName,
|
|
663
|
+
}) ??
|
|
664
|
+
evaluateArraySpecialCall(callee, rawArgs, context, {
|
|
665
|
+
error,
|
|
666
|
+
emptyValue,
|
|
667
|
+
numberValue,
|
|
668
|
+
stringValue,
|
|
669
|
+
stackScalar,
|
|
670
|
+
toRangeLike,
|
|
671
|
+
getRangeCell,
|
|
672
|
+
getBroadcastShape,
|
|
673
|
+
makeArrayStack,
|
|
674
|
+
applyLambda,
|
|
675
|
+
toPositiveInteger,
|
|
676
|
+
coerceScalarTextArgument,
|
|
677
|
+
coerceOptionalBooleanArgument,
|
|
678
|
+
coerceOptionalMatchModeArgument,
|
|
679
|
+
coerceOptionalPositiveIntegerArgument,
|
|
680
|
+
coerceOptionalTrimModeArgument,
|
|
681
|
+
isCellValueError,
|
|
682
|
+
isSingleCellValue,
|
|
683
|
+
}));
|
|
1298
684
|
}
|
|
1299
685
|
}
|
|
1300
686
|
function lowerNode(node, plan) {
|