@bilig/formula 0.7.8 → 0.8.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/binder-wasm-arity.d.ts +1 -0
- package/dist/binder-wasm-arity.js +541 -0
- package/dist/binder-wasm-arity.js.map +1 -0
- package/dist/binder-wasm-rules.d.ts +1 -2
- package/dist/binder-wasm-rules.js +2 -540
- package/dist/binder-wasm-rules.js.map +1 -1
- package/dist/builtins/cell-value-utils.d.ts +9 -0
- package/dist/builtins/cell-value-utils.js +63 -0
- package/dist/builtins/cell-value-utils.js.map +1 -0
- package/dist/builtins/datetime.d.ts +2 -10
- package/dist/builtins/datetime.js +5 -261
- package/dist/builtins/datetime.js.map +1 -1
- package/dist/builtins/excel-date.d.ts +16 -0
- package/dist/builtins/excel-date.js +199 -0
- package/dist/builtins/excel-date.js.map +1 -0
- package/dist/builtins/lookup-core-helpers.d.ts +50 -0
- package/dist/builtins/lookup-core-helpers.js +342 -0
- package/dist/builtins/lookup-core-helpers.js.map +1 -0
- package/dist/builtins/lookup.d.ts +3 -25
- package/dist/builtins/lookup.js +3 -341
- package/dist/builtins/lookup.js.map +1 -1
- package/dist/formula-reference-translation.d.ts +20 -0
- package/dist/formula-reference-translation.js +273 -0
- package/dist/formula-reference-translation.js.map +1 -0
- package/dist/formula-serializer.d.ts +2 -0
- package/dist/formula-serializer.js +74 -0
- package/dist/formula-serializer.js.map +1 -0
- package/dist/formula-sheet-rename.d.ts +14 -0
- package/dist/formula-sheet-rename.js +243 -0
- package/dist/formula-sheet-rename.js.map +1 -0
- package/dist/formula-structural-rewrite.d.ts +14 -0
- package/dist/formula-structural-rewrite.js +511 -0
- package/dist/formula-structural-rewrite.js.map +1 -0
- package/dist/formula-template-key.d.ts +2 -0
- package/dist/formula-template-key.js +58 -0
- package/dist/formula-template-key.js.map +1 -1
- package/dist/js-evaluator-cell-values.d.ts +5 -0
- package/dist/js-evaluator-cell-values.js +14 -0
- package/dist/js-evaluator-cell-values.js.map +1 -0
- package/dist/js-evaluator-context-special-calls.js +72 -0
- package/dist/js-evaluator-context-special-calls.js.map +1 -1
- package/dist/js-evaluator-runtime-helpers.d.ts +39 -0
- package/dist/js-evaluator-runtime-helpers.js +453 -0
- package/dist/js-evaluator-runtime-helpers.js.map +1 -0
- package/dist/js-evaluator-types.d.ts +178 -0
- package/dist/js-evaluator-types.js +2 -0
- package/dist/js-evaluator-types.js.map +1 -0
- package/dist/js-evaluator.d.ts +4 -178
- package/dist/js-evaluator.js +3 -463
- package/dist/js-evaluator.js.map +1 -1
- package/dist/translation-reference-utils.d.ts +42 -0
- package/dist/translation-reference-utils.js +178 -0
- package/dist/translation-reference-utils.js.map +1 -0
- package/dist/translation.d.ts +6 -32
- package/dist/translation.js +14 -1300
- package/dist/translation.js.map +1 -1
- package/package.json +2 -2
package/dist/js-evaluator.d.ts
CHANGED
|
@@ -1,182 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type CellValue } from '@bilig/protocol';
|
|
2
2
|
import type { FormulaNode } from './ast.js';
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
5
|
-
export
|
|
6
|
-
sheetName: string;
|
|
7
|
-
currentAddress?: string;
|
|
8
|
-
resolveCell: (sheetName: string, address: string) => CellValue;
|
|
9
|
-
resolveRange: (sheetName: string, start: string, end: string, refKind: 'cells' | 'rows' | 'cols') => CellValue[];
|
|
10
|
-
resolveName?: (name: string) => CellValue;
|
|
11
|
-
resolveFormula?: (sheetName: string, address: string) => string | undefined;
|
|
12
|
-
resolvePivotData?: (request: {
|
|
13
|
-
dataField: string;
|
|
14
|
-
sheetName: string;
|
|
15
|
-
address: string;
|
|
16
|
-
filters: ReadonlyArray<{
|
|
17
|
-
field: string;
|
|
18
|
-
item: CellValue;
|
|
19
|
-
}>;
|
|
20
|
-
}) => CellValue | undefined;
|
|
21
|
-
resolveMultipleOperations?: (request: {
|
|
22
|
-
formulaSheetName: string;
|
|
23
|
-
formulaAddress: string;
|
|
24
|
-
rowCellSheetName: string;
|
|
25
|
-
rowCellAddress: string;
|
|
26
|
-
rowReplacementSheetName: string;
|
|
27
|
-
rowReplacementAddress: string;
|
|
28
|
-
columnCellSheetName?: string;
|
|
29
|
-
columnCellAddress?: string;
|
|
30
|
-
columnReplacementSheetName?: string;
|
|
31
|
-
columnReplacementAddress?: string;
|
|
32
|
-
}) => CellValue | undefined;
|
|
33
|
-
resolveExactVectorMatch?: (request: {
|
|
34
|
-
lookupValue: CellValue;
|
|
35
|
-
sheetName: string;
|
|
36
|
-
start: string;
|
|
37
|
-
end: string;
|
|
38
|
-
startRow: number;
|
|
39
|
-
endRow: number;
|
|
40
|
-
startCol: number;
|
|
41
|
-
endCol: number;
|
|
42
|
-
searchMode: 1 | -1;
|
|
43
|
-
}) => ExactVectorMatchResult;
|
|
44
|
-
resolveApproximateVectorMatch?: (request: {
|
|
45
|
-
lookupValue: CellValue;
|
|
46
|
-
sheetName: string;
|
|
47
|
-
start: string;
|
|
48
|
-
end: string;
|
|
49
|
-
startRow: number;
|
|
50
|
-
endRow: number;
|
|
51
|
-
startCol: number;
|
|
52
|
-
endCol: number;
|
|
53
|
-
matchMode: 1 | -1;
|
|
54
|
-
}) => ApproximateVectorMatchResult;
|
|
55
|
-
noteRangeMaterialization?: (cellCount: number) => void;
|
|
56
|
-
noteExactLookupDirect?: () => void;
|
|
57
|
-
noteExactLookupFallback?: () => void;
|
|
58
|
-
listSheetNames?: () => string[];
|
|
59
|
-
resolveBuiltin?: (name: string) => ((...args: CellValue[]) => EvaluationResult) | undefined;
|
|
60
|
-
resolveLookupBuiltin?: (name: string) => LookupBuiltin | undefined;
|
|
61
|
-
}
|
|
62
|
-
export type ExactVectorMatchResult = {
|
|
63
|
-
handled: false;
|
|
64
|
-
} | {
|
|
65
|
-
handled: true;
|
|
66
|
-
position: number | undefined;
|
|
67
|
-
};
|
|
68
|
-
export type ApproximateVectorMatchResult = ExactVectorMatchResult;
|
|
69
|
-
export interface ReferenceOperand {
|
|
70
|
-
kind: 'cell' | 'range' | 'row' | 'col';
|
|
71
|
-
sheetName?: string;
|
|
72
|
-
address?: string;
|
|
73
|
-
start?: string;
|
|
74
|
-
end?: string;
|
|
75
|
-
refKind?: 'cells' | 'rows' | 'cols';
|
|
76
|
-
}
|
|
77
|
-
export type JsPlanInstruction = {
|
|
78
|
-
opcode: 'push-number';
|
|
79
|
-
value: number;
|
|
80
|
-
} | {
|
|
81
|
-
opcode: 'push-boolean';
|
|
82
|
-
value: boolean;
|
|
83
|
-
} | {
|
|
84
|
-
opcode: 'push-string';
|
|
85
|
-
value: string;
|
|
86
|
-
} | {
|
|
87
|
-
opcode: 'push-error';
|
|
88
|
-
code: ErrorCode;
|
|
89
|
-
} | {
|
|
90
|
-
opcode: 'push-name';
|
|
91
|
-
name: string;
|
|
92
|
-
} | {
|
|
93
|
-
opcode: 'push-cell';
|
|
94
|
-
sheetName?: string;
|
|
95
|
-
address: string;
|
|
96
|
-
} | {
|
|
97
|
-
opcode: 'push-range';
|
|
98
|
-
sheetName?: string;
|
|
99
|
-
start: string;
|
|
100
|
-
end: string;
|
|
101
|
-
refKind: 'cells' | 'rows' | 'cols';
|
|
102
|
-
} | {
|
|
103
|
-
opcode: 'lookup-exact-match';
|
|
104
|
-
callee: 'MATCH' | 'XMATCH';
|
|
105
|
-
sheetName?: string;
|
|
106
|
-
start: string;
|
|
107
|
-
end: string;
|
|
108
|
-
startRow: number;
|
|
109
|
-
endRow: number;
|
|
110
|
-
startCol: number;
|
|
111
|
-
endCol: number;
|
|
112
|
-
refKind: 'cells';
|
|
113
|
-
searchMode: 1 | -1;
|
|
114
|
-
} | {
|
|
115
|
-
opcode: 'lookup-approximate-match';
|
|
116
|
-
callee: 'MATCH' | 'XMATCH';
|
|
117
|
-
sheetName?: string;
|
|
118
|
-
start: string;
|
|
119
|
-
end: string;
|
|
120
|
-
startRow: number;
|
|
121
|
-
endRow: number;
|
|
122
|
-
startCol: number;
|
|
123
|
-
endCol: number;
|
|
124
|
-
refKind: 'cells';
|
|
125
|
-
matchMode: 1 | -1;
|
|
126
|
-
} | {
|
|
127
|
-
opcode: 'push-lambda';
|
|
128
|
-
params: string[];
|
|
129
|
-
body: JsPlanInstruction[];
|
|
130
|
-
} | {
|
|
131
|
-
opcode: 'unary';
|
|
132
|
-
operator: '+' | '-';
|
|
133
|
-
} | {
|
|
134
|
-
opcode: 'binary';
|
|
135
|
-
operator: '+' | '-' | '*' | '/' | '^' | '&' | '=' | '<>' | '>' | '>=' | '<' | '<=';
|
|
136
|
-
} | {
|
|
137
|
-
opcode: 'call';
|
|
138
|
-
callee: string;
|
|
139
|
-
argc: number;
|
|
140
|
-
argRefs?: Array<ReferenceOperand | undefined>;
|
|
141
|
-
} | {
|
|
142
|
-
opcode: 'invoke';
|
|
143
|
-
argc: number;
|
|
144
|
-
} | {
|
|
145
|
-
opcode: 'begin-scope';
|
|
146
|
-
} | {
|
|
147
|
-
opcode: 'bind-name';
|
|
148
|
-
name: string;
|
|
149
|
-
} | {
|
|
150
|
-
opcode: 'end-scope';
|
|
151
|
-
} | {
|
|
152
|
-
opcode: 'jump-if-false';
|
|
153
|
-
target: number;
|
|
154
|
-
} | {
|
|
155
|
-
opcode: 'jump';
|
|
156
|
-
target: number;
|
|
157
|
-
} | {
|
|
158
|
-
opcode: 'return';
|
|
159
|
-
};
|
|
160
|
-
export type StackValue = {
|
|
161
|
-
kind: 'scalar';
|
|
162
|
-
value: CellValue;
|
|
163
|
-
} | {
|
|
164
|
-
kind: 'omitted';
|
|
165
|
-
} | {
|
|
166
|
-
kind: 'range';
|
|
167
|
-
values: CellValue[];
|
|
168
|
-
refKind: 'cells' | 'rows' | 'cols';
|
|
169
|
-
rows: number;
|
|
170
|
-
cols: number;
|
|
171
|
-
sheetName?: string;
|
|
172
|
-
start?: string;
|
|
173
|
-
end?: string;
|
|
174
|
-
} | {
|
|
175
|
-
kind: 'lambda';
|
|
176
|
-
params: string[];
|
|
177
|
-
body: JsPlanInstruction[];
|
|
178
|
-
scopes: Array<Map<string, StackValue>>;
|
|
179
|
-
} | ArrayValue;
|
|
3
|
+
import type { EvaluationContext, JsPlanInstruction } from './js-evaluator-types.js';
|
|
4
|
+
import { type EvaluationResult } from './runtime-values.js';
|
|
5
|
+
export type { ApproximateVectorMatchResult, EvaluationContext, ExactVectorMatchResult, JsPlanInstruction, ReferenceOperand, StackValue, } from './js-evaluator-types.js';
|
|
180
6
|
export { lowerToPlan } from './js-plan-lowering.js';
|
|
181
7
|
export declare function evaluatePlanResult(plan: readonly JsPlanInstruction[], context: EvaluationContext): EvaluationResult;
|
|
182
8
|
export declare function evaluatePlan(plan: readonly JsPlanInstruction[], context: EvaluationContext): CellValue;
|
package/dist/js-evaluator.js
CHANGED
|
@@ -1,476 +1,16 @@
|
|
|
1
|
-
import { ErrorCode, ValueTag
|
|
1
|
+
import { ErrorCode, ValueTag } from '@bilig/protocol';
|
|
2
2
|
import { parseRangeAddress } from './addressing.js';
|
|
3
3
|
import { getBuiltin } from './builtins.js';
|
|
4
4
|
import { getLookupBuiltin } from './builtins/lookup.js';
|
|
5
5
|
import { evaluateArraySpecialCall } from './js-evaluator-array-special-calls.js';
|
|
6
|
+
import { emptyValue, error, numberValue, stringValue } from './js-evaluator-cell-values.js';
|
|
6
7
|
import { evaluateContextSpecialCall } from './js-evaluator-context-special-calls.js';
|
|
7
8
|
import { absoluteAddress, cellTypeCode, currentCellReference, referenceColumnNumber, referenceRowNumber, referenceSheetName, referenceTopLeftAddress, sheetIndexByName, sheetNames, } from './js-evaluator-reference-context.js';
|
|
9
|
+
import { cloneScopes, cloneStackValue, coerceOptionalBooleanArgument, coerceOptionalMatchModeArgument, coerceOptionalPositiveIntegerArgument, coerceOptionalTrimModeArgument, coerceScalarTextArgument, evaluateBinary, getBroadcastShape, getRangeCell, isCellValueError, isSingleCellValue, makeArrayStack, matrixFromStackValue, normalizeScopeName, popArgument, popScalar, scalarIntegerArgument, stackScalar, toEvaluationResult, toNumber, toPositiveInteger, toRangeArgument, toRangeLike, toStringValue, truthy, vectorIntegerArgument, } from './js-evaluator-runtime-helpers.js';
|
|
8
10
|
import { evaluateWorkbookSpecialCall } from './js-evaluator-workbook-special-calls.js';
|
|
9
11
|
import { lowerToPlan } from './js-plan-lowering.js';
|
|
10
12
|
import { isArrayValue, scalarFromEvaluationResult } from './runtime-values.js';
|
|
11
13
|
export { lowerToPlan } from './js-plan-lowering.js';
|
|
12
|
-
function emptyValue() {
|
|
13
|
-
return { tag: ValueTag.Empty };
|
|
14
|
-
}
|
|
15
|
-
function error(code) {
|
|
16
|
-
return { tag: ValueTag.Error, code };
|
|
17
|
-
}
|
|
18
|
-
function numberValue(value) {
|
|
19
|
-
return { tag: ValueTag.Number, value };
|
|
20
|
-
}
|
|
21
|
-
function stringValue(value) {
|
|
22
|
-
return { tag: ValueTag.String, value, stringId: 0 };
|
|
23
|
-
}
|
|
24
|
-
function toNumber(value) {
|
|
25
|
-
switch (value.tag) {
|
|
26
|
-
case ValueTag.Number:
|
|
27
|
-
return value.value;
|
|
28
|
-
case ValueTag.Boolean:
|
|
29
|
-
return value.value ? 1 : 0;
|
|
30
|
-
case ValueTag.Empty:
|
|
31
|
-
return 0;
|
|
32
|
-
case ValueTag.String:
|
|
33
|
-
case ValueTag.Error:
|
|
34
|
-
return undefined;
|
|
35
|
-
default:
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function toStringValue(value) {
|
|
40
|
-
switch (value.tag) {
|
|
41
|
-
case ValueTag.Empty:
|
|
42
|
-
return '';
|
|
43
|
-
case ValueTag.Number:
|
|
44
|
-
return String(value.value);
|
|
45
|
-
case ValueTag.Boolean:
|
|
46
|
-
return value.value ? 'TRUE' : 'FALSE';
|
|
47
|
-
case ValueTag.String:
|
|
48
|
-
return value.value;
|
|
49
|
-
case ValueTag.Error:
|
|
50
|
-
return formatErrorCode(value.code);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function isTextLike(value) {
|
|
54
|
-
return value.tag === ValueTag.String || value.tag === ValueTag.Empty;
|
|
55
|
-
}
|
|
56
|
-
function compareText(left, right) {
|
|
57
|
-
const normalizedLeft = left.toUpperCase();
|
|
58
|
-
const normalizedRight = right.toUpperCase();
|
|
59
|
-
if (normalizedLeft === normalizedRight) {
|
|
60
|
-
return 0;
|
|
61
|
-
}
|
|
62
|
-
return normalizedLeft < normalizedRight ? -1 : 1;
|
|
63
|
-
}
|
|
64
|
-
function compareScalars(left, right) {
|
|
65
|
-
if (isTextLike(left) && isTextLike(right)) {
|
|
66
|
-
return compareText(toStringValue(left), toStringValue(right));
|
|
67
|
-
}
|
|
68
|
-
const leftNum = comparableNumber(left);
|
|
69
|
-
const rightNum = comparableNumber(right);
|
|
70
|
-
if (leftNum === undefined || rightNum === undefined) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
if (leftNum === rightNum) {
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
76
|
-
return leftNum < rightNum ? -1 : 1;
|
|
77
|
-
}
|
|
78
|
-
function comparableNumber(value) {
|
|
79
|
-
switch (value.tag) {
|
|
80
|
-
case ValueTag.Number:
|
|
81
|
-
return value.value;
|
|
82
|
-
case ValueTag.Boolean:
|
|
83
|
-
return value.value ? 1 : 0;
|
|
84
|
-
case ValueTag.Empty:
|
|
85
|
-
return 0;
|
|
86
|
-
case ValueTag.String: {
|
|
87
|
-
const trimmed = value.value.trim();
|
|
88
|
-
if (trimmed === '') {
|
|
89
|
-
return 0;
|
|
90
|
-
}
|
|
91
|
-
const parsed = Number(trimmed);
|
|
92
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
93
|
-
}
|
|
94
|
-
case ValueTag.Error:
|
|
95
|
-
default:
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function truthy(value) {
|
|
100
|
-
return (toNumber(value) ?? 0) !== 0;
|
|
101
|
-
}
|
|
102
|
-
function popScalar(stack) {
|
|
103
|
-
const value = stack.pop();
|
|
104
|
-
if (!value) {
|
|
105
|
-
return error(ErrorCode.Value);
|
|
106
|
-
}
|
|
107
|
-
if (value.kind === 'scalar') {
|
|
108
|
-
return value.value;
|
|
109
|
-
}
|
|
110
|
-
if (value.kind === 'omitted') {
|
|
111
|
-
return error(ErrorCode.Value);
|
|
112
|
-
}
|
|
113
|
-
if (value.kind === 'lambda') {
|
|
114
|
-
return error(ErrorCode.Value);
|
|
115
|
-
}
|
|
116
|
-
return value.values[0] ?? emptyValue();
|
|
117
|
-
}
|
|
118
|
-
function popArgument(stack) {
|
|
119
|
-
return stack.pop() ?? { kind: 'scalar', value: error(ErrorCode.Value) };
|
|
120
|
-
}
|
|
121
|
-
function toEvaluationResult(value) {
|
|
122
|
-
if (!value) {
|
|
123
|
-
return error(ErrorCode.Value);
|
|
124
|
-
}
|
|
125
|
-
if (value.kind === 'scalar') {
|
|
126
|
-
return value.value;
|
|
127
|
-
}
|
|
128
|
-
if (value.kind === 'omitted') {
|
|
129
|
-
return error(ErrorCode.Value);
|
|
130
|
-
}
|
|
131
|
-
if (value.kind === 'lambda') {
|
|
132
|
-
return error(ErrorCode.Value);
|
|
133
|
-
}
|
|
134
|
-
if (value.kind === 'range') {
|
|
135
|
-
return {
|
|
136
|
-
kind: 'array',
|
|
137
|
-
rows: value.rows,
|
|
138
|
-
cols: value.cols,
|
|
139
|
-
values: value.values,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
|
-
function cloneStackValue(value) {
|
|
145
|
-
if (value.kind === 'scalar') {
|
|
146
|
-
return { kind: 'scalar', value: value.value };
|
|
147
|
-
}
|
|
148
|
-
if (value.kind === 'omitted') {
|
|
149
|
-
return { kind: 'omitted' };
|
|
150
|
-
}
|
|
151
|
-
if (value.kind === 'range') {
|
|
152
|
-
return {
|
|
153
|
-
kind: 'range',
|
|
154
|
-
values: value.values,
|
|
155
|
-
refKind: value.refKind,
|
|
156
|
-
rows: value.rows,
|
|
157
|
-
cols: value.cols,
|
|
158
|
-
...(value.sheetName ? { sheetName: value.sheetName } : {}),
|
|
159
|
-
...(value.start ? { start: value.start } : {}),
|
|
160
|
-
...(value.end ? { end: value.end } : {}),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if (value.kind === 'lambda') {
|
|
164
|
-
return {
|
|
165
|
-
kind: 'lambda',
|
|
166
|
-
params: [...value.params],
|
|
167
|
-
body: value.body,
|
|
168
|
-
scopes: cloneScopes(value.scopes),
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
return { kind: 'array', values: value.values, rows: value.rows, cols: value.cols };
|
|
172
|
-
}
|
|
173
|
-
function cloneScopes(scopes) {
|
|
174
|
-
return scopes.map((scope) => new Map([...scope.entries()].map(([name, value]) => [name, cloneStackValue(value)])));
|
|
175
|
-
}
|
|
176
|
-
function toRangeLike(value) {
|
|
177
|
-
if (value.kind === 'omitted') {
|
|
178
|
-
return { kind: 'range', values: [error(ErrorCode.Value)], rows: 1, cols: 1, refKind: 'cells' };
|
|
179
|
-
}
|
|
180
|
-
if (value.kind === 'lambda') {
|
|
181
|
-
return { kind: 'range', values: [error(ErrorCode.Value)], rows: 1, cols: 1, refKind: 'cells' };
|
|
182
|
-
}
|
|
183
|
-
if (value.kind === 'range') {
|
|
184
|
-
return value;
|
|
185
|
-
}
|
|
186
|
-
if (value.kind === 'array') {
|
|
187
|
-
return {
|
|
188
|
-
kind: 'range',
|
|
189
|
-
values: value.values,
|
|
190
|
-
rows: value.rows,
|
|
191
|
-
cols: value.cols,
|
|
192
|
-
refKind: 'cells',
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
return { kind: 'range', values: [value.value], rows: 1, cols: 1, refKind: 'cells' };
|
|
196
|
-
}
|
|
197
|
-
function scalarBinary(operator, leftValue, rightValue) {
|
|
198
|
-
if (leftValue.tag === ValueTag.Error) {
|
|
199
|
-
return leftValue;
|
|
200
|
-
}
|
|
201
|
-
if (rightValue.tag === ValueTag.Error) {
|
|
202
|
-
return rightValue;
|
|
203
|
-
}
|
|
204
|
-
if (operator === '&') {
|
|
205
|
-
return {
|
|
206
|
-
tag: ValueTag.String,
|
|
207
|
-
value: `${toStringValue(leftValue)}${toStringValue(rightValue)}`,
|
|
208
|
-
stringId: 0,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
if (['+', '-', '*', '/', '^'].includes(operator)) {
|
|
212
|
-
const left = toNumber(leftValue);
|
|
213
|
-
const right = toNumber(rightValue);
|
|
214
|
-
if (left === undefined || right === undefined) {
|
|
215
|
-
return error(ErrorCode.Value);
|
|
216
|
-
}
|
|
217
|
-
if (operator === '/' && right === 0) {
|
|
218
|
-
return error(ErrorCode.Div0);
|
|
219
|
-
}
|
|
220
|
-
const value = operator === '+'
|
|
221
|
-
? left + right
|
|
222
|
-
: operator === '-'
|
|
223
|
-
? left - right
|
|
224
|
-
: operator === '*'
|
|
225
|
-
? left * right
|
|
226
|
-
: operator === '/'
|
|
227
|
-
? left / right
|
|
228
|
-
: left ** right;
|
|
229
|
-
return { tag: ValueTag.Number, value };
|
|
230
|
-
}
|
|
231
|
-
const comparison = compareScalars(leftValue, rightValue);
|
|
232
|
-
if (comparison === undefined) {
|
|
233
|
-
return error(ErrorCode.Value);
|
|
234
|
-
}
|
|
235
|
-
return {
|
|
236
|
-
tag: ValueTag.Boolean,
|
|
237
|
-
value: operator === '='
|
|
238
|
-
? comparison === 0
|
|
239
|
-
: operator === '<>'
|
|
240
|
-
? comparison !== 0
|
|
241
|
-
: operator === '>'
|
|
242
|
-
? comparison > 0
|
|
243
|
-
: operator === '>='
|
|
244
|
-
? comparison >= 0
|
|
245
|
-
: operator === '<'
|
|
246
|
-
? comparison < 0
|
|
247
|
-
: comparison <= 0,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
function evaluateBinary(operator, leftValue, rightValue) {
|
|
251
|
-
if (leftValue.kind === 'scalar' && rightValue.kind === 'scalar') {
|
|
252
|
-
return scalarBinary(operator, leftValue.value, rightValue.value);
|
|
253
|
-
}
|
|
254
|
-
const leftRange = toRangeLike(leftValue);
|
|
255
|
-
const rightRange = toRangeLike(rightValue);
|
|
256
|
-
const rows = leftRange.rows === rightRange.rows
|
|
257
|
-
? leftRange.rows
|
|
258
|
-
: leftRange.rows === 1
|
|
259
|
-
? rightRange.rows
|
|
260
|
-
: rightRange.rows === 1
|
|
261
|
-
? leftRange.rows
|
|
262
|
-
: 0;
|
|
263
|
-
const cols = leftRange.cols === rightRange.cols
|
|
264
|
-
? leftRange.cols
|
|
265
|
-
: leftRange.cols === 1
|
|
266
|
-
? rightRange.cols
|
|
267
|
-
: rightRange.cols === 1
|
|
268
|
-
? leftRange.cols
|
|
269
|
-
: 0;
|
|
270
|
-
if (rows === 0 || cols === 0) {
|
|
271
|
-
return error(ErrorCode.Value);
|
|
272
|
-
}
|
|
273
|
-
const values = [];
|
|
274
|
-
for (let row = 0; row < rows; row += 1) {
|
|
275
|
-
for (let col = 0; col < cols; col += 1) {
|
|
276
|
-
const leftIndex = Math.min(row, leftRange.rows - 1) * leftRange.cols + Math.min(col, leftRange.cols - 1);
|
|
277
|
-
const rightIndex = Math.min(row, rightRange.rows - 1) * rightRange.cols + Math.min(col, rightRange.cols - 1);
|
|
278
|
-
values.push(scalarBinary(operator, leftRange.values[leftIndex] ?? emptyValue(), rightRange.values[rightIndex] ?? emptyValue()));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return rows === 1 && cols === 1 ? (values[0] ?? emptyValue()) : { kind: 'array', values, rows, cols };
|
|
282
|
-
}
|
|
283
|
-
function stackScalar(value) {
|
|
284
|
-
return { kind: 'scalar', value };
|
|
285
|
-
}
|
|
286
|
-
function normalizeScopeName(name) {
|
|
287
|
-
return name.toUpperCase();
|
|
288
|
-
}
|
|
289
|
-
function isSingleCellValue(value) {
|
|
290
|
-
if (value.kind === 'scalar') {
|
|
291
|
-
return value.value;
|
|
292
|
-
}
|
|
293
|
-
if (value.kind === 'omitted') {
|
|
294
|
-
return undefined;
|
|
295
|
-
}
|
|
296
|
-
if (value.kind === 'lambda') {
|
|
297
|
-
return undefined;
|
|
298
|
-
}
|
|
299
|
-
return value.rows * value.cols === 1 ? (value.values[0] ?? emptyValue()) : undefined;
|
|
300
|
-
}
|
|
301
|
-
function toRangeArgument(value) {
|
|
302
|
-
if (value.kind === 'scalar') {
|
|
303
|
-
return value.value;
|
|
304
|
-
}
|
|
305
|
-
if (value.kind === 'omitted') {
|
|
306
|
-
return error(ErrorCode.Value);
|
|
307
|
-
}
|
|
308
|
-
if (value.kind === 'lambda') {
|
|
309
|
-
return error(ErrorCode.Value);
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
kind: 'range',
|
|
313
|
-
values: value.values,
|
|
314
|
-
refKind: value.kind === 'range' ? value.refKind : 'cells',
|
|
315
|
-
rows: value.rows,
|
|
316
|
-
cols: value.cols,
|
|
317
|
-
...(value.kind === 'range' && value.sheetName ? { sheetName: value.sheetName } : {}),
|
|
318
|
-
...(value.kind === 'range' && value.start ? { start: value.start } : {}),
|
|
319
|
-
...(value.kind === 'range' && value.end ? { end: value.end } : {}),
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
function toPositiveInteger(value) {
|
|
323
|
-
if (value === undefined) {
|
|
324
|
-
return undefined;
|
|
325
|
-
}
|
|
326
|
-
const scalar = isSingleCellValue(value);
|
|
327
|
-
const numeric = scalar ? toNumber(scalar) : undefined;
|
|
328
|
-
if (numeric === undefined || !Number.isFinite(numeric)) {
|
|
329
|
-
return undefined;
|
|
330
|
-
}
|
|
331
|
-
const integer = Math.trunc(numeric);
|
|
332
|
-
return integer >= 1 ? integer : undefined;
|
|
333
|
-
}
|
|
334
|
-
function getRangeCell(range, row, col) {
|
|
335
|
-
return range.values[row * range.cols + col] ?? emptyValue();
|
|
336
|
-
}
|
|
337
|
-
function getBroadcastShape(values) {
|
|
338
|
-
const ranges = values.map(toRangeLike);
|
|
339
|
-
const rows = Math.max(...ranges.map((range) => range.rows));
|
|
340
|
-
const cols = Math.max(...ranges.map((range) => range.cols));
|
|
341
|
-
const compatible = ranges.every((range) => (range.rows === rows || range.rows === 1) && (range.cols === cols || range.cols === 1));
|
|
342
|
-
return compatible ? { rows, cols } : undefined;
|
|
343
|
-
}
|
|
344
|
-
function coerceScalarTextArgument(value) {
|
|
345
|
-
if (value === undefined) {
|
|
346
|
-
return error(ErrorCode.Value);
|
|
347
|
-
}
|
|
348
|
-
const scalar = isSingleCellValue(value);
|
|
349
|
-
if (!scalar) {
|
|
350
|
-
return error(ErrorCode.Value);
|
|
351
|
-
}
|
|
352
|
-
if (scalar.tag === ValueTag.Error) {
|
|
353
|
-
return scalar;
|
|
354
|
-
}
|
|
355
|
-
return toStringValue(scalar);
|
|
356
|
-
}
|
|
357
|
-
function coerceOptionalBooleanArgument(value, fallback) {
|
|
358
|
-
if (value === undefined) {
|
|
359
|
-
return fallback;
|
|
360
|
-
}
|
|
361
|
-
const scalar = isSingleCellValue(value);
|
|
362
|
-
if (!scalar) {
|
|
363
|
-
return error(ErrorCode.Value);
|
|
364
|
-
}
|
|
365
|
-
if (scalar.tag === ValueTag.Error) {
|
|
366
|
-
return scalar;
|
|
367
|
-
}
|
|
368
|
-
if (scalar.tag === ValueTag.Boolean) {
|
|
369
|
-
return scalar.value;
|
|
370
|
-
}
|
|
371
|
-
const numeric = toNumber(scalar);
|
|
372
|
-
return numeric === undefined ? error(ErrorCode.Value) : numeric !== 0;
|
|
373
|
-
}
|
|
374
|
-
function coerceOptionalMatchModeArgument(value, fallback) {
|
|
375
|
-
if (value === undefined) {
|
|
376
|
-
return fallback;
|
|
377
|
-
}
|
|
378
|
-
const scalar = isSingleCellValue(value);
|
|
379
|
-
if (!scalar) {
|
|
380
|
-
return error(ErrorCode.Value);
|
|
381
|
-
}
|
|
382
|
-
if (scalar.tag === ValueTag.Error) {
|
|
383
|
-
return scalar;
|
|
384
|
-
}
|
|
385
|
-
const numeric = toNumber(scalar);
|
|
386
|
-
if (numeric === undefined || !Number.isFinite(numeric)) {
|
|
387
|
-
return error(ErrorCode.Value);
|
|
388
|
-
}
|
|
389
|
-
const integer = Math.trunc(numeric);
|
|
390
|
-
return integer === 0 || integer === 1 ? integer : error(ErrorCode.Value);
|
|
391
|
-
}
|
|
392
|
-
function coerceOptionalPositiveIntegerArgument(value, fallback) {
|
|
393
|
-
if (value === undefined) {
|
|
394
|
-
return fallback;
|
|
395
|
-
}
|
|
396
|
-
const scalar = isSingleCellValue(value);
|
|
397
|
-
if (!scalar) {
|
|
398
|
-
return error(ErrorCode.Value);
|
|
399
|
-
}
|
|
400
|
-
if (scalar.tag === ValueTag.Error) {
|
|
401
|
-
return scalar;
|
|
402
|
-
}
|
|
403
|
-
const numeric = toNumber(scalar);
|
|
404
|
-
if (numeric === undefined || !Number.isFinite(numeric)) {
|
|
405
|
-
return error(ErrorCode.Value);
|
|
406
|
-
}
|
|
407
|
-
const integer = Math.trunc(numeric);
|
|
408
|
-
return integer >= 1 ? integer : error(ErrorCode.Value);
|
|
409
|
-
}
|
|
410
|
-
function coerceOptionalTrimModeArgument(value, fallback) {
|
|
411
|
-
if (value === undefined) {
|
|
412
|
-
return fallback;
|
|
413
|
-
}
|
|
414
|
-
const scalar = isSingleCellValue(value);
|
|
415
|
-
if (!scalar) {
|
|
416
|
-
return error(ErrorCode.Value);
|
|
417
|
-
}
|
|
418
|
-
if (scalar.tag === ValueTag.Error) {
|
|
419
|
-
return scalar;
|
|
420
|
-
}
|
|
421
|
-
const numeric = toNumber(scalar);
|
|
422
|
-
if (numeric === undefined || !Number.isFinite(numeric)) {
|
|
423
|
-
return error(ErrorCode.Value);
|
|
424
|
-
}
|
|
425
|
-
const integer = Math.trunc(numeric);
|
|
426
|
-
switch (integer) {
|
|
427
|
-
case 0:
|
|
428
|
-
case 1:
|
|
429
|
-
case 2:
|
|
430
|
-
case 3:
|
|
431
|
-
return integer;
|
|
432
|
-
default:
|
|
433
|
-
return error(ErrorCode.Value);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
function isCellValueError(value) {
|
|
437
|
-
return typeof value === 'object' && value !== null && 'tag' in value;
|
|
438
|
-
}
|
|
439
|
-
function makeArrayStack(rows, cols, values) {
|
|
440
|
-
return { kind: 'array', rows, cols, values };
|
|
441
|
-
}
|
|
442
|
-
function matrixFromStackValue(value) {
|
|
443
|
-
if (value.kind === 'omitted' || value.kind === 'lambda') {
|
|
444
|
-
return undefined;
|
|
445
|
-
}
|
|
446
|
-
if (value.kind === 'scalar') {
|
|
447
|
-
return { rows: 1, cols: 1, values: [value.value] };
|
|
448
|
-
}
|
|
449
|
-
return { rows: value.rows, cols: value.cols, values: value.values };
|
|
450
|
-
}
|
|
451
|
-
function scalarIntegerArgument(value) {
|
|
452
|
-
const scalar = value ? isSingleCellValue(value) : undefined;
|
|
453
|
-
const numeric = scalar ? toNumber(scalar) : undefined;
|
|
454
|
-
return numeric === undefined || !Number.isFinite(numeric) ? undefined : Math.trunc(numeric);
|
|
455
|
-
}
|
|
456
|
-
function vectorIntegerArgument(value) {
|
|
457
|
-
if (!value) {
|
|
458
|
-
return undefined;
|
|
459
|
-
}
|
|
460
|
-
const matrix = matrixFromStackValue(value);
|
|
461
|
-
if (!matrix || !(matrix.rows === 1 || matrix.cols === 1)) {
|
|
462
|
-
return undefined;
|
|
463
|
-
}
|
|
464
|
-
const values = [];
|
|
465
|
-
for (let index = 0; index < matrix.rows * matrix.cols; index += 1) {
|
|
466
|
-
const numeric = toNumber(matrix.values[index] ?? emptyValue());
|
|
467
|
-
if (numeric === undefined || !Number.isFinite(numeric)) {
|
|
468
|
-
return undefined;
|
|
469
|
-
}
|
|
470
|
-
values.push(Math.trunc(numeric));
|
|
471
|
-
}
|
|
472
|
-
return values;
|
|
473
|
-
}
|
|
474
14
|
function aggregateRangeSubset(functionArg, subset, context, totalSet) {
|
|
475
15
|
if (functionArg.kind === 'lambda') {
|
|
476
16
|
const args = [makeArrayStack(Math.max(subset.length, 1), 1, [...subset])];
|