@bilig/formula 0.1.83 → 0.1.85
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/addressing.d.ts +6 -6
- package/dist/addressing.js +16 -16
- package/dist/addressing.js.map +1 -1
- package/dist/ast.d.ts +18 -18
- package/dist/binder-wasm-rules.d.ts +2 -2
- package/dist/binder-wasm-rules.js +677 -724
- package/dist/binder-wasm-rules.js.map +1 -1
- package/dist/binder.d.ts +2 -2
- package/dist/binder.js +133 -137
- package/dist/binder.js.map +1 -1
- package/dist/builtin-capabilities.d.ts +4 -4
- package/dist/builtin-capabilities.js +777 -816
- package/dist/builtin-capabilities.js.map +1 -1
- package/dist/builtins/complex.d.ts +3 -3
- package/dist/builtins/complex.js +20 -48
- package/dist/builtins/complex.js.map +1 -1
- package/dist/builtins/convert.d.ts +1 -1
- package/dist/builtins/convert.js +180 -181
- package/dist/builtins/convert.js.map +1 -1
- package/dist/builtins/datetime.d.ts +1 -1
- package/dist/builtins/datetime.js +60 -72
- package/dist/builtins/datetime.js.map +1 -1
- package/dist/builtins/distribution-builtins.d.ts +2 -2
- package/dist/builtins/distribution-builtins.js +62 -112
- package/dist/builtins/distribution-builtins.js.map +1 -1
- package/dist/builtins/distributions.js +17 -54
- package/dist/builtins/distributions.js.map +1 -1
- package/dist/builtins/financial-builtins.d.ts +2 -2
- package/dist/builtins/financial-builtins.js +9 -42
- package/dist/builtins/financial-builtins.js.map +1 -1
- package/dist/builtins/financial.js +4 -18
- package/dist/builtins/financial.js.map +1 -1
- package/dist/builtins/fixed-income-builtins.d.ts +2 -2
- package/dist/builtins/fixed-income-builtins.js +18 -60
- package/dist/builtins/fixed-income-builtins.js.map +1 -1
- package/dist/builtins/fixed-income.js +25 -96
- package/dist/builtins/fixed-income.js.map +1 -1
- package/dist/builtins/formatting.js +9 -9
- package/dist/builtins/formatting.js.map +1 -1
- package/dist/builtins/logical.d.ts +1 -1
- package/dist/builtins/logical.js +5 -5
- package/dist/builtins/logical.js.map +1 -1
- package/dist/builtins/lookup-array-shape-builtins.d.ts +3 -3
- package/dist/builtins/lookup-array-shape-builtins.js +8 -15
- package/dist/builtins/lookup-array-shape-builtins.js.map +1 -1
- package/dist/builtins/lookup-criteria-builtins.d.ts +2 -2
- package/dist/builtins/lookup-criteria-builtins.js +1 -1
- package/dist/builtins/lookup-criteria-builtins.js.map +1 -1
- package/dist/builtins/lookup-database-builtins.d.ts +2 -2
- package/dist/builtins/lookup-database-builtins.js +20 -24
- package/dist/builtins/lookup-database-builtins.js.map +1 -1
- package/dist/builtins/lookup-financial-builtins.d.ts +3 -3
- package/dist/builtins/lookup-financial-builtins.js +14 -30
- package/dist/builtins/lookup-financial-builtins.js.map +1 -1
- package/dist/builtins/lookup-hypothesis-builtins.d.ts +2 -2
- package/dist/builtins/lookup-hypothesis-builtins.js +23 -58
- package/dist/builtins/lookup-hypothesis-builtins.js.map +1 -1
- package/dist/builtins/lookup-matrix-builtins.d.ts +3 -3
- package/dist/builtins/lookup-matrix-builtins.js +5 -3
- package/dist/builtins/lookup-matrix-builtins.js.map +1 -1
- package/dist/builtins/lookup-order-statistics-builtins.d.ts +3 -3
- package/dist/builtins/lookup-order-statistics-builtins.js +18 -30
- package/dist/builtins/lookup-order-statistics-builtins.js.map +1 -1
- package/dist/builtins/lookup-reference-builtins.d.ts +2 -2
- package/dist/builtins/lookup-reference-builtins.js +10 -26
- package/dist/builtins/lookup-reference-builtins.js.map +1 -1
- package/dist/builtins/lookup-regression-builtins.d.ts +2 -2
- package/dist/builtins/lookup-regression-builtins.js +52 -69
- package/dist/builtins/lookup-regression-builtins.js.map +1 -1
- package/dist/builtins/lookup-sort-filter-builtins.d.ts +3 -3
- package/dist/builtins/lookup-sort-filter-builtins.js +5 -13
- package/dist/builtins/lookup-sort-filter-builtins.js.map +1 -1
- package/dist/builtins/lookup.d.ts +5 -5
- package/dist/builtins/lookup.js +61 -73
- package/dist/builtins/lookup.js.map +1 -1
- package/dist/builtins/math-builtins.d.ts +2 -2
- package/dist/builtins/math-builtins.js +11 -17
- package/dist/builtins/math-builtins.js.map +1 -1
- package/dist/builtins/numeric.d.ts +2 -2
- package/dist/builtins/numeric.js +2 -2
- package/dist/builtins/numeric.js.map +1 -1
- package/dist/builtins/placeholder.d.ts +1 -1
- package/dist/builtins/placeholder.js +505 -505
- package/dist/builtins/placeholder.js.map +1 -1
- package/dist/builtins/radix.d.ts +2 -2
- package/dist/builtins/radix.js +33 -39
- package/dist/builtins/radix.js.map +1 -1
- package/dist/builtins/statistical-builtins.d.ts +2 -2
- package/dist/builtins/statistical-builtins.js +29 -56
- package/dist/builtins/statistical-builtins.js.map +1 -1
- package/dist/builtins/statistics.d.ts +1 -1
- package/dist/builtins/statistics.js +3 -7
- package/dist/builtins/statistics.js.map +1 -1
- package/dist/builtins/text-core-builtins.d.ts +2 -2
- package/dist/builtins/text-core-builtins.js +110 -110
- package/dist/builtins/text-core-builtins.js.map +1 -1
- package/dist/builtins/text-format-builtins.d.ts +2 -2
- package/dist/builtins/text-format-builtins.js +109 -141
- package/dist/builtins/text-format-builtins.js.map +1 -1
- package/dist/builtins/text-search-builtins.d.ts +2 -2
- package/dist/builtins/text-search-builtins.js +37 -44
- package/dist/builtins/text-search-builtins.js.map +1 -1
- package/dist/builtins/text.d.ts +2 -2
- package/dist/builtins/text.js +19 -29
- package/dist/builtins/text.js.map +1 -1
- package/dist/builtins.d.ts +3 -3
- package/dist/builtins.js +55 -75
- package/dist/builtins.js.map +1 -1
- package/dist/compatibility.js +425 -430
- package/dist/compatibility.js.map +1 -1
- package/dist/compiler.d.ts +7 -7
- package/dist/compiler.js +212 -228
- package/dist/compiler.js.map +1 -1
- package/dist/external-function-adapter.d.ts +6 -6
- package/dist/external-function-adapter.js +3 -3
- package/dist/external-function-adapter.js.map +1 -1
- package/dist/formula-template-key.js +25 -26
- package/dist/formula-template-key.js.map +1 -1
- package/dist/group-pivot-evaluator.d.ts +2 -2
- package/dist/group-pivot-evaluator.js +27 -32
- package/dist/group-pivot-evaluator.js.map +1 -1
- package/dist/index.d.ts +20 -20
- package/dist/index.js +20 -20
- package/dist/index.js.map +1 -1
- package/dist/js-evaluator-array-special-calls.d.ts +3 -3
- package/dist/js-evaluator-array-special-calls.js +25 -34
- package/dist/js-evaluator-array-special-calls.js.map +1 -1
- package/dist/js-evaluator-context-special-calls.d.ts +2 -2
- package/dist/js-evaluator-context-special-calls.js +23 -27
- package/dist/js-evaluator-context-special-calls.js.map +1 -1
- package/dist/js-evaluator-reference-context.d.ts +2 -2
- package/dist/js-evaluator-reference-context.js +22 -24
- package/dist/js-evaluator-reference-context.js.map +1 -1
- package/dist/js-evaluator-workbook-special-calls.d.ts +2 -2
- package/dist/js-evaluator-workbook-special-calls.js +18 -24
- package/dist/js-evaluator-workbook-special-calls.js.map +1 -1
- package/dist/js-evaluator.d.ts +40 -40
- package/dist/js-evaluator.js +135 -147
- package/dist/js-evaluator.js.map +1 -1
- package/dist/js-plan-lowering.d.ts +2 -2
- package/dist/js-plan-lowering.js +103 -108
- package/dist/js-plan-lowering.js.map +1 -1
- package/dist/lexer.d.ts +1 -1
- package/dist/lexer.js +34 -43
- package/dist/lexer.js.map +1 -1
- package/dist/optimizer.d.ts +1 -1
- package/dist/optimizer.js +99 -108
- package/dist/optimizer.js.map +1 -1
- package/dist/parser.d.ts +1 -1
- package/dist/parser.js +129 -136
- package/dist/parser.js.map +1 -1
- package/dist/program-arena.js.map +1 -1
- package/dist/runtime-inventory.d.ts +2 -2
- package/dist/runtime-inventory.js +14 -20
- package/dist/runtime-inventory.js.map +1 -1
- package/dist/runtime-values.d.ts +4 -4
- package/dist/runtime-values.js +2 -2
- package/dist/runtime-values.js.map +1 -1
- package/dist/special-call-rewrites.d.ts +1 -1
- package/dist/special-call-rewrites.js +14 -18
- package/dist/special-call-rewrites.js.map +1 -1
- package/dist/translation.d.ts +7 -7
- package/dist/translation.js +323 -366
- package/dist/translation.js.map +1 -1
- package/package.json +2 -2
package/dist/compiler.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { BuiltinId, FormulaMode, Opcode } from
|
|
2
|
-
import { columnToIndex, formatRangeAddress, parseCellAddress, parseRangeAddress
|
|
3
|
-
import { getNativeGroupedArrayKind } from
|
|
4
|
-
import { bindFormula, encodeBuiltin } from
|
|
5
|
-
import { lowerToPlan } from
|
|
6
|
-
import { optimizeFormula } from
|
|
7
|
-
import { parseFormula } from
|
|
8
|
-
import { rewriteSpecialCall } from
|
|
1
|
+
import { BuiltinId, FormulaMode, Opcode } from '@bilig/protocol';
|
|
2
|
+
import { columnToIndex, formatRangeAddress, parseCellAddress, parseRangeAddress } from './addressing.js';
|
|
3
|
+
import { getNativeGroupedArrayKind } from './binder-wasm-rules.js';
|
|
4
|
+
import { bindFormula, encodeBuiltin } from './binder.js';
|
|
5
|
+
import { lowerToPlan } from './js-evaluator.js';
|
|
6
|
+
import { optimizeFormula } from './optimizer.js';
|
|
7
|
+
import { parseFormula } from './parser.js';
|
|
8
|
+
import { rewriteSpecialCall } from './special-call-rewrites.js';
|
|
9
9
|
function encodeInstruction(opcode, operand = 0) {
|
|
10
10
|
return (opcode << 24) | (operand & 0x00ff_ffff);
|
|
11
11
|
}
|
|
12
12
|
const SIMPLE_CELL_REF_RE = /^\$?[A-Z]+\$?[1-9][0-9]*$/;
|
|
13
13
|
const SIMPLE_NUMBER_RE = /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
14
14
|
const SIMPLE_BINARY_RE = /^\s*(\$?[A-Z]+\$?[1-9][0-9]*|[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)\s*([+*])\s*(\$?[A-Z]+\$?[1-9][0-9]*|[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)\s*$/;
|
|
15
|
-
const VOLATILE_BUILTINS = new Set([
|
|
15
|
+
const VOLATILE_BUILTINS = new Set(['TODAY', 'NOW', 'RAND', 'RANDBETWEEN', 'RANDARRAY']);
|
|
16
16
|
function producesSpillResult(node) {
|
|
17
17
|
switch (node.kind) {
|
|
18
|
-
case
|
|
19
|
-
case
|
|
20
|
-
case
|
|
21
|
-
case
|
|
22
|
-
case
|
|
23
|
-
case
|
|
24
|
-
case
|
|
25
|
-
case
|
|
26
|
-
case
|
|
27
|
-
case
|
|
28
|
-
case
|
|
18
|
+
case 'NumberLiteral':
|
|
19
|
+
case 'BooleanLiteral':
|
|
20
|
+
case 'StringLiteral':
|
|
21
|
+
case 'ErrorLiteral':
|
|
22
|
+
case 'NameRef':
|
|
23
|
+
case 'StructuredRef':
|
|
24
|
+
case 'CellRef':
|
|
25
|
+
case 'SpillRef':
|
|
26
|
+
case 'RowRef':
|
|
27
|
+
case 'ColumnRef':
|
|
28
|
+
case 'InvokeExpr':
|
|
29
29
|
return false;
|
|
30
|
-
case
|
|
30
|
+
case 'RangeRef':
|
|
31
31
|
return true;
|
|
32
|
-
case
|
|
32
|
+
case 'UnaryExpr':
|
|
33
33
|
return producesSpillResult(node.argument);
|
|
34
|
-
case
|
|
34
|
+
case 'BinaryExpr':
|
|
35
35
|
return producesSpillResult(node.left) || producesSpillResult(node.right);
|
|
36
|
-
case
|
|
37
|
-
if (node.callee.toUpperCase() ===
|
|
36
|
+
case 'CallExpr':
|
|
37
|
+
if (node.callee.toUpperCase() === 'CHOOSE') {
|
|
38
38
|
return node.args.slice(1).some((arg) => producesSpillResult(arg));
|
|
39
39
|
}
|
|
40
|
-
if (node.callee.toUpperCase() ===
|
|
40
|
+
if (node.callee.toUpperCase() === 'TREND' || node.callee.toUpperCase() === 'GROWTH') {
|
|
41
41
|
const shapeArg = node.args[2] ?? node.args[1] ?? node.args[0];
|
|
42
42
|
if (shapeArg === undefined) {
|
|
43
43
|
return false;
|
|
@@ -45,58 +45,58 @@ function producesSpillResult(node) {
|
|
|
45
45
|
return producesSpillResult(shapeArg);
|
|
46
46
|
}
|
|
47
47
|
return [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
48
|
+
'SEQUENCE',
|
|
49
|
+
'EXPAND',
|
|
50
|
+
'LINEST',
|
|
51
|
+
'LOGEST',
|
|
52
|
+
'OFFSET',
|
|
53
|
+
'TAKE',
|
|
54
|
+
'DROP',
|
|
55
|
+
'CHOOSECOLS',
|
|
56
|
+
'CHOOSEROWS',
|
|
57
|
+
'SORT',
|
|
58
|
+
'SORTBY',
|
|
59
|
+
'TOCOL',
|
|
60
|
+
'TOROW',
|
|
61
|
+
'WRAPROWS',
|
|
62
|
+
'WRAPCOLS',
|
|
63
|
+
'FILTER',
|
|
64
|
+
'UNIQUE',
|
|
65
|
+
'FREQUENCY',
|
|
66
|
+
'MODE.MULT',
|
|
67
|
+
'TEXTSPLIT',
|
|
68
|
+
'TRIMRANGE',
|
|
69
|
+
'GROUPBY',
|
|
70
|
+
'PIVOTBY',
|
|
71
|
+
'MAKEARRAY',
|
|
72
|
+
'MAP',
|
|
73
|
+
'SCAN',
|
|
74
|
+
'BYROW',
|
|
75
|
+
'BYCOL',
|
|
76
|
+
'RANDARRAY',
|
|
77
|
+
'MUNIT',
|
|
78
|
+
'MINVERSE',
|
|
79
|
+
'MMULT',
|
|
80
80
|
].includes(node.callee.toUpperCase());
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
function analyzeVolatileMetadata(node) {
|
|
84
84
|
switch (node.kind) {
|
|
85
|
-
case
|
|
86
|
-
case
|
|
87
|
-
case
|
|
88
|
-
case
|
|
89
|
-
case
|
|
90
|
-
case
|
|
91
|
-
case
|
|
92
|
-
case
|
|
93
|
-
case
|
|
94
|
-
case
|
|
95
|
-
case
|
|
85
|
+
case 'NumberLiteral':
|
|
86
|
+
case 'BooleanLiteral':
|
|
87
|
+
case 'StringLiteral':
|
|
88
|
+
case 'ErrorLiteral':
|
|
89
|
+
case 'NameRef':
|
|
90
|
+
case 'StructuredRef':
|
|
91
|
+
case 'CellRef':
|
|
92
|
+
case 'SpillRef':
|
|
93
|
+
case 'RowRef':
|
|
94
|
+
case 'ColumnRef':
|
|
95
|
+
case 'RangeRef':
|
|
96
96
|
return { volatile: false, randCallCount: 0 };
|
|
97
|
-
case
|
|
97
|
+
case 'UnaryExpr':
|
|
98
98
|
return analyzeVolatileMetadata(node.argument);
|
|
99
|
-
case
|
|
99
|
+
case 'BinaryExpr': {
|
|
100
100
|
const left = analyzeVolatileMetadata(node.left);
|
|
101
101
|
const right = analyzeVolatileMetadata(node.right);
|
|
102
102
|
return {
|
|
@@ -104,14 +104,14 @@ function analyzeVolatileMetadata(node) {
|
|
|
104
104
|
randCallCount: left.randCallCount + right.randCallCount,
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
case
|
|
107
|
+
case 'CallExpr': {
|
|
108
108
|
const rewritten = rewriteSpecialCall(node);
|
|
109
109
|
if (rewritten) {
|
|
110
110
|
return analyzeVolatileMetadata(rewritten);
|
|
111
111
|
}
|
|
112
112
|
const callee = node.callee.toUpperCase();
|
|
113
113
|
let volatile = VOLATILE_BUILTINS.has(callee);
|
|
114
|
-
let randCallCount = callee ===
|
|
114
|
+
let randCallCount = callee === 'RAND' ? 1 : 0;
|
|
115
115
|
node.args.forEach((arg) => {
|
|
116
116
|
const child = analyzeVolatileMetadata(arg);
|
|
117
117
|
volatile = volatile || child.volatile;
|
|
@@ -119,7 +119,7 @@ function analyzeVolatileMetadata(node) {
|
|
|
119
119
|
});
|
|
120
120
|
return { volatile, randCallCount };
|
|
121
121
|
}
|
|
122
|
-
case
|
|
122
|
+
case 'InvokeExpr': {
|
|
123
123
|
const callee = analyzeVolatileMetadata(node.callee);
|
|
124
124
|
const args = node.args.map(analyzeVolatileMetadata);
|
|
125
125
|
return {
|
|
@@ -145,19 +145,19 @@ function emitRangeRef(node, state) {
|
|
|
145
145
|
state.program.push(encodeInstruction(Opcode.PushRange, index));
|
|
146
146
|
}
|
|
147
147
|
function isCellRangeNode(node) {
|
|
148
|
-
if (node.kind !==
|
|
148
|
+
if (node.kind !== 'RangeRef') {
|
|
149
149
|
return false;
|
|
150
150
|
}
|
|
151
151
|
try {
|
|
152
|
-
const sheetPrefix = node.sheetName ? `${node.sheetName}!` :
|
|
153
|
-
return parseRangeAddress(`${sheetPrefix}${node.start}:${node.end}`).kind ===
|
|
152
|
+
const sheetPrefix = node.sheetName ? `${node.sheetName}!` : '';
|
|
153
|
+
return parseRangeAddress(`${sheetPrefix}${node.start}:${node.end}`).kind === 'cells';
|
|
154
154
|
}
|
|
155
155
|
catch {
|
|
156
156
|
return false;
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
function emitArgument(node, state) {
|
|
160
|
-
if (node.kind ===
|
|
160
|
+
if (node.kind === 'RangeRef') {
|
|
161
161
|
emitRangeRef(node, state);
|
|
162
162
|
return 1;
|
|
163
163
|
}
|
|
@@ -165,44 +165,39 @@ function emitArgument(node, state) {
|
|
|
165
165
|
return 1;
|
|
166
166
|
}
|
|
167
167
|
const AXIS_AGGREGATE_CODES = new Map([
|
|
168
|
-
[
|
|
169
|
-
[
|
|
170
|
-
[
|
|
171
|
-
[
|
|
172
|
-
[
|
|
173
|
-
[
|
|
174
|
-
[
|
|
168
|
+
['SUM', 1],
|
|
169
|
+
['AVERAGE', 2],
|
|
170
|
+
['AVG', 2],
|
|
171
|
+
['MIN', 3],
|
|
172
|
+
['MAX', 4],
|
|
173
|
+
['COUNT', 5],
|
|
174
|
+
['COUNTA', 6],
|
|
175
175
|
]);
|
|
176
176
|
function getNativeAxisAggregateCode(node) {
|
|
177
|
-
if (node.kind !==
|
|
178
|
-
node.callee.toUpperCase() !== "LAMBDA" ||
|
|
179
|
-
node.args.length !== 2) {
|
|
177
|
+
if (node.kind !== 'CallExpr' || node.callee.toUpperCase() !== 'LAMBDA' || node.args.length !== 2) {
|
|
180
178
|
return null;
|
|
181
179
|
}
|
|
182
180
|
const [param, body] = node.args;
|
|
183
|
-
if (param?.kind !==
|
|
181
|
+
if (param?.kind !== 'NameRef' || body?.kind !== 'CallExpr' || body.args.length !== 1) {
|
|
184
182
|
return null;
|
|
185
183
|
}
|
|
186
184
|
const aggregateCode = AXIS_AGGREGATE_CODES.get(body.callee.toUpperCase());
|
|
187
185
|
if (aggregateCode === undefined) {
|
|
188
186
|
return null;
|
|
189
187
|
}
|
|
190
|
-
return body.args[0]?.kind ===
|
|
191
|
-
body.args[0].name.trim().toUpperCase() === param.name.trim().toUpperCase()
|
|
188
|
+
return body.args[0]?.kind === 'NameRef' && body.args[0].name.trim().toUpperCase() === param.name.trim().toUpperCase()
|
|
192
189
|
? aggregateCode
|
|
193
190
|
: null;
|
|
194
191
|
}
|
|
195
192
|
function getNativeRunningFoldCode(node) {
|
|
196
|
-
if (node.kind !==
|
|
197
|
-
node.callee.toUpperCase() !== "LAMBDA" ||
|
|
198
|
-
node.args.length !== 3) {
|
|
193
|
+
if (node.kind !== 'CallExpr' || node.callee.toUpperCase() !== 'LAMBDA' || node.args.length !== 3) {
|
|
199
194
|
return null;
|
|
200
195
|
}
|
|
201
196
|
const [acc, value, body] = node.args;
|
|
202
|
-
if (acc?.kind !==
|
|
197
|
+
if (acc?.kind !== 'NameRef' || value?.kind !== 'NameRef' || body?.kind !== 'BinaryExpr') {
|
|
203
198
|
return null;
|
|
204
199
|
}
|
|
205
|
-
const foldCode = body.operator ===
|
|
200
|
+
const foldCode = body.operator === '+' ? 1 : body.operator === '*' ? 2 : null;
|
|
206
201
|
if (foldCode === null) {
|
|
207
202
|
return null;
|
|
208
203
|
}
|
|
@@ -210,47 +205,44 @@ function getNativeRunningFoldCode(node) {
|
|
|
210
205
|
const right = body.right;
|
|
211
206
|
const accName = acc.name.trim().toUpperCase();
|
|
212
207
|
const valueName = value.name.trim().toUpperCase();
|
|
213
|
-
return left.kind ===
|
|
214
|
-
right.kind ===
|
|
215
|
-
((left.name.trim().toUpperCase() === accName &&
|
|
216
|
-
right.name.trim().toUpperCase() === valueName) ||
|
|
208
|
+
return left.kind === 'NameRef' &&
|
|
209
|
+
right.kind === 'NameRef' &&
|
|
210
|
+
((left.name.trim().toUpperCase() === accName && right.name.trim().toUpperCase() === valueName) ||
|
|
217
211
|
(left.name.trim().toUpperCase() === valueName && right.name.trim().toUpperCase() === accName))
|
|
218
212
|
? foldCode
|
|
219
213
|
: null;
|
|
220
214
|
}
|
|
221
215
|
function isNativeMakearraySumLambda(node) {
|
|
222
|
-
if (node.kind !==
|
|
223
|
-
node.callee.toUpperCase() !== "LAMBDA" ||
|
|
224
|
-
node.args.length !== 3) {
|
|
216
|
+
if (node.kind !== 'CallExpr' || node.callee.toUpperCase() !== 'LAMBDA' || node.args.length !== 3) {
|
|
225
217
|
return false;
|
|
226
218
|
}
|
|
227
219
|
const [rowParam, colParam, body] = node.args;
|
|
228
|
-
if (rowParam?.kind !==
|
|
220
|
+
if (rowParam?.kind !== 'NameRef' || colParam?.kind !== 'NameRef' || body?.kind !== 'BinaryExpr') {
|
|
229
221
|
return false;
|
|
230
222
|
}
|
|
231
|
-
if (body.operator !==
|
|
223
|
+
if (body.operator !== '+') {
|
|
232
224
|
return false;
|
|
233
225
|
}
|
|
234
226
|
const left = body.left;
|
|
235
227
|
const right = body.right;
|
|
236
228
|
const rowName = rowParam.name.trim().toUpperCase();
|
|
237
229
|
const colName = colParam.name.trim().toUpperCase();
|
|
238
|
-
return (left.kind ===
|
|
239
|
-
right.kind ===
|
|
230
|
+
return (left.kind === 'NameRef' &&
|
|
231
|
+
right.kind === 'NameRef' &&
|
|
240
232
|
((left.name.trim().toUpperCase() === rowName && right.name.trim().toUpperCase() === colName) ||
|
|
241
233
|
(left.name.trim().toUpperCase() === colName && right.name.trim().toUpperCase() === rowName)));
|
|
242
234
|
}
|
|
243
235
|
function emitNode(node, state) {
|
|
244
236
|
switch (node.kind) {
|
|
245
|
-
case
|
|
237
|
+
case 'NumberLiteral': {
|
|
246
238
|
const index = state.constants.push(node.value) - 1;
|
|
247
239
|
state.program.push(encodeInstruction(Opcode.PushNumber, index));
|
|
248
240
|
return;
|
|
249
241
|
}
|
|
250
|
-
case
|
|
242
|
+
case 'BooleanLiteral':
|
|
251
243
|
state.program.push(encodeInstruction(Opcode.PushBoolean, node.value ? 1 : 0));
|
|
252
244
|
return;
|
|
253
|
-
case
|
|
245
|
+
case 'StringLiteral':
|
|
254
246
|
{
|
|
255
247
|
let index = state.strings.indexOf(node.value);
|
|
256
248
|
if (index === -1)
|
|
@@ -258,48 +250,48 @@ function emitNode(node, state) {
|
|
|
258
250
|
state.program.push(encodeInstruction(Opcode.PushString, index));
|
|
259
251
|
}
|
|
260
252
|
return;
|
|
261
|
-
case
|
|
253
|
+
case 'ErrorLiteral':
|
|
262
254
|
state.program.push(encodeInstruction(Opcode.PushError, node.code));
|
|
263
255
|
return;
|
|
264
|
-
case
|
|
265
|
-
case
|
|
266
|
-
case
|
|
267
|
-
throw new Error(
|
|
268
|
-
case
|
|
256
|
+
case 'NameRef':
|
|
257
|
+
case 'StructuredRef':
|
|
258
|
+
case 'SpillRef':
|
|
259
|
+
throw new Error('Defined names are not supported on the wasm fast path');
|
|
260
|
+
case 'CellRef': {
|
|
269
261
|
emitCellRef(node.ref, node.sheetName, state);
|
|
270
262
|
return;
|
|
271
263
|
}
|
|
272
|
-
case
|
|
264
|
+
case 'RangeRef':
|
|
273
265
|
emitRangeRef(node, state);
|
|
274
266
|
return;
|
|
275
|
-
case
|
|
276
|
-
case
|
|
277
|
-
throw new Error(
|
|
278
|
-
case
|
|
267
|
+
case 'RowRef':
|
|
268
|
+
case 'ColumnRef':
|
|
269
|
+
throw new Error('Row and column references must appear inside a range');
|
|
270
|
+
case 'UnaryExpr':
|
|
279
271
|
emitNode(node.argument, state);
|
|
280
|
-
if (node.operator ===
|
|
272
|
+
if (node.operator === '-') {
|
|
281
273
|
state.program.push(encodeInstruction(Opcode.Neg));
|
|
282
274
|
}
|
|
283
275
|
return;
|
|
284
|
-
case
|
|
276
|
+
case 'BinaryExpr':
|
|
285
277
|
emitNode(node.left, state);
|
|
286
278
|
emitNode(node.right, state);
|
|
287
279
|
state.program.push(encodeInstruction({
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
280
|
+
'+': Opcode.Add,
|
|
281
|
+
'-': Opcode.Sub,
|
|
282
|
+
'*': Opcode.Mul,
|
|
283
|
+
'/': Opcode.Div,
|
|
284
|
+
'^': Opcode.Pow,
|
|
285
|
+
'&': Opcode.Concat,
|
|
286
|
+
'=': Opcode.Eq,
|
|
287
|
+
'<>': Opcode.Neq,
|
|
288
|
+
'>': Opcode.Gt,
|
|
289
|
+
'>=': Opcode.Gte,
|
|
290
|
+
'<': Opcode.Lt,
|
|
291
|
+
'<=': Opcode.Lte,
|
|
300
292
|
}[node.operator]));
|
|
301
293
|
return;
|
|
302
|
-
case
|
|
294
|
+
case 'CallExpr':
|
|
303
295
|
{
|
|
304
296
|
const rewritten = rewriteSpecialCall(node);
|
|
305
297
|
if (rewritten) {
|
|
@@ -308,22 +300,22 @@ function emitNode(node, state) {
|
|
|
308
300
|
}
|
|
309
301
|
const callee = node.callee.toUpperCase();
|
|
310
302
|
const nativeGroupedArrayKind = getNativeGroupedArrayKind(node);
|
|
311
|
-
if (nativeGroupedArrayKind ===
|
|
303
|
+
if (nativeGroupedArrayKind === 'groupby-sum-canonical') {
|
|
312
304
|
emitArgument(node.args[0], state);
|
|
313
305
|
emitArgument(node.args[1], state);
|
|
314
306
|
state.program.push(encodeInstruction(Opcode.CallBuiltin, (BuiltinId.GroupbySumCanonical << 8) | 2));
|
|
315
307
|
return;
|
|
316
308
|
}
|
|
317
|
-
if (nativeGroupedArrayKind ===
|
|
309
|
+
if (nativeGroupedArrayKind === 'pivotby-sum-canonical') {
|
|
318
310
|
emitArgument(node.args[0], state);
|
|
319
311
|
emitArgument(node.args[1], state);
|
|
320
312
|
emitArgument(node.args[2], state);
|
|
321
313
|
state.program.push(encodeInstruction(Opcode.CallBuiltin, (BuiltinId.PivotbySumCanonical << 8) | 3));
|
|
322
314
|
return;
|
|
323
315
|
}
|
|
324
|
-
if (callee ===
|
|
316
|
+
if (callee === 'IF') {
|
|
325
317
|
if (node.args.length !== 3) {
|
|
326
|
-
throw new Error(
|
|
318
|
+
throw new Error('IF requires exactly three arguments on the wasm fast path');
|
|
327
319
|
}
|
|
328
320
|
emitNode(node.args[0], state);
|
|
329
321
|
const jumpIfFalseIndex = state.program.push(encodeInstruction(Opcode.JumpIfFalse, 0)) - 1;
|
|
@@ -336,27 +328,24 @@ function emitNode(node, state) {
|
|
|
336
328
|
state.program[jumpIndex] = encodeInstruction(Opcode.Jump, end);
|
|
337
329
|
return;
|
|
338
330
|
}
|
|
339
|
-
if ((callee ===
|
|
331
|
+
if ((callee === 'BYROW' || callee === 'BYCOL') && node.args.length === 2) {
|
|
340
332
|
const lambda = node.args[1];
|
|
341
333
|
const aggregateCode = getNativeAxisAggregateCode(lambda);
|
|
342
334
|
if (aggregateCode !== null) {
|
|
343
335
|
const aggregateIndex = state.constants.push(aggregateCode) - 1;
|
|
344
336
|
state.program.push(encodeInstruction(Opcode.PushNumber, aggregateIndex));
|
|
345
337
|
emitArgument(node.args[0], state);
|
|
346
|
-
state.program.push(encodeInstruction(Opcode.CallBuiltin, ((callee ===
|
|
347
|
-
2));
|
|
338
|
+
state.program.push(encodeInstruction(Opcode.CallBuiltin, ((callee === 'BYROW' ? BuiltinId.ByrowAggregate : BuiltinId.BycolAggregate) << 8) | 2));
|
|
348
339
|
return;
|
|
349
340
|
}
|
|
350
341
|
}
|
|
351
|
-
if (callee ===
|
|
352
|
-
node.args.length === 3 &&
|
|
353
|
-
isNativeMakearraySumLambda(node.args[2])) {
|
|
342
|
+
if (callee === 'MAKEARRAY' && node.args.length === 3 && isNativeMakearraySumLambda(node.args[2])) {
|
|
354
343
|
emitArgument(node.args[0], state);
|
|
355
344
|
emitArgument(node.args[1], state);
|
|
356
345
|
state.program.push(encodeInstruction(Opcode.CallBuiltin, (BuiltinId.MakearraySum << 8) | 2));
|
|
357
346
|
return;
|
|
358
347
|
}
|
|
359
|
-
if (callee ===
|
|
348
|
+
if (callee === 'REDUCE' || callee === 'SCAN') {
|
|
360
349
|
const sourceArg = node.args.length === 3 ? node.args[1] : node.args[0];
|
|
361
350
|
const lambdaArg = node.args.length === 3 ? node.args[2] : node.args[1];
|
|
362
351
|
const initialArg = node.args.length === 3 ? node.args[0] : undefined;
|
|
@@ -370,7 +359,7 @@ function emitNode(node, state) {
|
|
|
370
359
|
argc += emitArgument(initialArg, state);
|
|
371
360
|
}
|
|
372
361
|
argc += emitArgument(sourceArg, state);
|
|
373
|
-
state.program.push(encodeInstruction(Opcode.CallBuiltin, ((callee ===
|
|
362
|
+
state.program.push(encodeInstruction(Opcode.CallBuiltin, ((callee === 'REDUCE'
|
|
374
363
|
? foldCode === 1
|
|
375
364
|
? BuiltinId.ReduceSum
|
|
376
365
|
: BuiltinId.ReduceProduct
|
|
@@ -383,10 +372,7 @@ function emitNode(node, state) {
|
|
|
383
372
|
}
|
|
384
373
|
}
|
|
385
374
|
const rangeArg = node.args[0];
|
|
386
|
-
if (callee ===
|
|
387
|
-
node.args.length === 1 &&
|
|
388
|
-
rangeArg &&
|
|
389
|
-
isCellRangeNode(rangeArg)) {
|
|
375
|
+
if (callee === 'PHONETIC' && node.args.length === 1 && rangeArg && isCellRangeNode(rangeArg)) {
|
|
390
376
|
emitCellRef(rangeArg.start, rangeArg.sheetName, state);
|
|
391
377
|
state.program.push(encodeInstruction(Opcode.CallBuiltin, (BuiltinId.Phonetic << 8) | 1));
|
|
392
378
|
return;
|
|
@@ -398,8 +384,8 @@ function emitNode(node, state) {
|
|
|
398
384
|
state.program.push(encodeInstruction(Opcode.CallBuiltin, (encodeBuiltin(callee) << 8) | argc));
|
|
399
385
|
}
|
|
400
386
|
return;
|
|
401
|
-
case
|
|
402
|
-
throw new Error(
|
|
387
|
+
case 'InvokeExpr':
|
|
388
|
+
throw new Error('Lambda invocation is not supported on the wasm fast path');
|
|
403
389
|
}
|
|
404
390
|
}
|
|
405
391
|
const CELL_REFERENCE_PARTS_RE = /^(\$?)([A-Z]+)(\$?)([1-9][0-9]*)$/i;
|
|
@@ -410,41 +396,41 @@ function computeMaxStackDepth(plan) {
|
|
|
410
396
|
let max = 0;
|
|
411
397
|
for (const instruction of plan) {
|
|
412
398
|
switch (instruction.opcode) {
|
|
413
|
-
case
|
|
414
|
-
case
|
|
415
|
-
case
|
|
416
|
-
case
|
|
417
|
-
case
|
|
418
|
-
case
|
|
419
|
-
case
|
|
420
|
-
case
|
|
399
|
+
case 'push-number':
|
|
400
|
+
case 'push-boolean':
|
|
401
|
+
case 'push-string':
|
|
402
|
+
case 'push-error':
|
|
403
|
+
case 'push-name':
|
|
404
|
+
case 'push-cell':
|
|
405
|
+
case 'push-range':
|
|
406
|
+
case 'push-lambda':
|
|
421
407
|
current += 1;
|
|
422
408
|
break;
|
|
423
|
-
case
|
|
424
|
-
case
|
|
409
|
+
case 'lookup-exact-match':
|
|
410
|
+
case 'lookup-approximate-match':
|
|
425
411
|
break;
|
|
426
|
-
case
|
|
412
|
+
case 'binary':
|
|
427
413
|
current -= 1;
|
|
428
414
|
break;
|
|
429
|
-
case
|
|
415
|
+
case 'call':
|
|
430
416
|
current -= instruction.argc;
|
|
431
417
|
current += 1;
|
|
432
418
|
break;
|
|
433
|
-
case
|
|
419
|
+
case 'invoke':
|
|
434
420
|
current -= instruction.argc;
|
|
435
421
|
current += 1;
|
|
436
422
|
break;
|
|
437
|
-
case
|
|
423
|
+
case 'jump-if-false':
|
|
438
424
|
current -= 1;
|
|
439
425
|
break;
|
|
440
|
-
case
|
|
426
|
+
case 'bind-name':
|
|
441
427
|
current -= 1;
|
|
442
428
|
break;
|
|
443
|
-
case
|
|
444
|
-
case
|
|
445
|
-
case
|
|
446
|
-
case
|
|
447
|
-
case
|
|
429
|
+
case 'unary':
|
|
430
|
+
case 'begin-scope':
|
|
431
|
+
case 'end-scope':
|
|
432
|
+
case 'jump':
|
|
433
|
+
case 'return':
|
|
448
434
|
break;
|
|
449
435
|
}
|
|
450
436
|
max = Math.max(max, current);
|
|
@@ -455,20 +441,20 @@ function parseSimpleOperand(source) {
|
|
|
455
441
|
const trimmed = source.trim();
|
|
456
442
|
if (SIMPLE_CELL_REF_RE.test(trimmed)) {
|
|
457
443
|
return {
|
|
458
|
-
kind:
|
|
444
|
+
kind: 'CellRef',
|
|
459
445
|
ref: trimmed,
|
|
460
446
|
};
|
|
461
447
|
}
|
|
462
448
|
if (SIMPLE_NUMBER_RE.test(trimmed)) {
|
|
463
449
|
return {
|
|
464
|
-
kind:
|
|
450
|
+
kind: 'NumberLiteral',
|
|
465
451
|
value: Number(trimmed),
|
|
466
452
|
};
|
|
467
453
|
}
|
|
468
454
|
return null;
|
|
469
455
|
}
|
|
470
456
|
function stripSheetQualifier(reference) {
|
|
471
|
-
const bang = reference.lastIndexOf(
|
|
457
|
+
const bang = reference.lastIndexOf('!');
|
|
472
458
|
return bang === -1 ? reference.trim() : reference.slice(bang + 1).trim();
|
|
473
459
|
}
|
|
474
460
|
function parseCellReferenceParts(reference) {
|
|
@@ -480,23 +466,23 @@ function parseCellReferenceParts(reference) {
|
|
|
480
466
|
return {
|
|
481
467
|
row: Number.parseInt(rowText, 10) - 1,
|
|
482
468
|
col: columnToIndex(columnText),
|
|
483
|
-
rowAbsolute: rowAbsolute ===
|
|
484
|
-
colAbsolute: colAbsolute ===
|
|
469
|
+
rowAbsolute: rowAbsolute === '$',
|
|
470
|
+
colAbsolute: colAbsolute === '$',
|
|
485
471
|
};
|
|
486
472
|
}
|
|
487
473
|
function parseAxisReferenceParts(reference, kind) {
|
|
488
|
-
const match = (kind ===
|
|
474
|
+
const match = (kind === 'row' ? ROW_REFERENCE_PARTS_RE : COLUMN_REFERENCE_PARTS_RE).exec(stripSheetQualifier(reference));
|
|
489
475
|
if (!match) {
|
|
490
476
|
return undefined;
|
|
491
477
|
}
|
|
492
|
-
return kind ===
|
|
478
|
+
return kind === 'row'
|
|
493
479
|
? {
|
|
494
480
|
index: Number.parseInt(match[2], 10) - 1,
|
|
495
|
-
absolute: match[1] ===
|
|
481
|
+
absolute: match[1] === '$',
|
|
496
482
|
}
|
|
497
483
|
: {
|
|
498
484
|
index: columnToIndex(match[2]),
|
|
499
|
-
absolute: match[1] ===
|
|
485
|
+
absolute: match[1] === '$',
|
|
500
486
|
};
|
|
501
487
|
}
|
|
502
488
|
function buildParsedCellReferenceInfo(reference) {
|
|
@@ -505,7 +491,7 @@ function buildParsedCellReferenceInfo(reference) {
|
|
|
505
491
|
return {
|
|
506
492
|
address: reference,
|
|
507
493
|
...(parsedCell.sheetName !== undefined ? { sheetName: parsedCell.sheetName } : {}),
|
|
508
|
-
...(reference.includes(
|
|
494
|
+
...(reference.includes('!') ? { explicitSheet: true } : {}),
|
|
509
495
|
row: parsedCell.row,
|
|
510
496
|
col: parsedCell.col,
|
|
511
497
|
...(parts
|
|
@@ -518,14 +504,14 @@ function buildParsedCellReferenceInfo(reference) {
|
|
|
518
504
|
}
|
|
519
505
|
function buildParsedRangeReferenceInfo(reference) {
|
|
520
506
|
const parsedRange = parseRangeAddress(reference);
|
|
521
|
-
const bounds = parsedRange.kind ===
|
|
507
|
+
const bounds = parsedRange.kind === 'cells'
|
|
522
508
|
? {
|
|
523
509
|
startRow: parsedRange.start.row,
|
|
524
510
|
endRow: parsedRange.end.row,
|
|
525
511
|
startCol: parsedRange.start.col,
|
|
526
512
|
endCol: parsedRange.end.col,
|
|
527
513
|
}
|
|
528
|
-
: parsedRange.kind ===
|
|
514
|
+
: parsedRange.kind === 'rows'
|
|
529
515
|
? {
|
|
530
516
|
startRow: parsedRange.start.row,
|
|
531
517
|
endRow: parsedRange.end.row,
|
|
@@ -538,32 +524,32 @@ function buildParsedRangeReferenceInfo(reference) {
|
|
|
538
524
|
startCol: parsedRange.start.col,
|
|
539
525
|
endCol: parsedRange.end.col,
|
|
540
526
|
};
|
|
541
|
-
const separator = reference.indexOf(
|
|
527
|
+
const separator = reference.indexOf(':');
|
|
542
528
|
const rawStart = reference.slice(0, separator).trim();
|
|
543
529
|
const rawEnd = reference.slice(separator + 1).trim();
|
|
544
|
-
const cellStart = parsedRange.kind ===
|
|
545
|
-
const cellEnd = parsedRange.kind ===
|
|
546
|
-
const rowStart = parsedRange.kind ===
|
|
547
|
-
const rowEnd = parsedRange.kind ===
|
|
548
|
-
const colStart = parsedRange.kind ===
|
|
549
|
-
const colEnd = parsedRange.kind ===
|
|
530
|
+
const cellStart = parsedRange.kind === 'cells' ? parseCellReferenceParts(rawStart) : undefined;
|
|
531
|
+
const cellEnd = parsedRange.kind === 'cells' ? parseCellReferenceParts(rawEnd) : undefined;
|
|
532
|
+
const rowStart = parsedRange.kind === 'rows' ? parseAxisReferenceParts(rawStart, 'row') : undefined;
|
|
533
|
+
const rowEnd = parsedRange.kind === 'rows' ? parseAxisReferenceParts(rawEnd, 'row') : undefined;
|
|
534
|
+
const colStart = parsedRange.kind === 'cols' ? parseAxisReferenceParts(rawStart, 'column') : undefined;
|
|
535
|
+
const colEnd = parsedRange.kind === 'cols' ? parseAxisReferenceParts(rawEnd, 'column') : undefined;
|
|
550
536
|
return {
|
|
551
537
|
address: reference,
|
|
552
|
-
kind:
|
|
538
|
+
kind: 'range',
|
|
553
539
|
refKind: parsedRange.kind,
|
|
554
540
|
...(parsedRange.sheetName !== undefined ? { sheetName: parsedRange.sheetName } : {}),
|
|
555
|
-
...(rawStart.includes(
|
|
541
|
+
...(rawStart.includes('!') ? { explicitSheet: true } : {}),
|
|
556
542
|
startAddress: parsedRange.start.text,
|
|
557
543
|
endAddress: parsedRange.end.text,
|
|
558
544
|
...bounds,
|
|
559
|
-
...(parsedRange.kind ===
|
|
545
|
+
...(parsedRange.kind === 'cells'
|
|
560
546
|
? {
|
|
561
547
|
startRowAbsolute: cellStart?.rowAbsolute ?? false,
|
|
562
548
|
endRowAbsolute: cellEnd?.rowAbsolute ?? false,
|
|
563
549
|
startColAbsolute: cellStart?.colAbsolute ?? false,
|
|
564
550
|
endColAbsolute: cellEnd?.colAbsolute ?? false,
|
|
565
551
|
}
|
|
566
|
-
: parsedRange.kind ===
|
|
552
|
+
: parsedRange.kind === 'rows'
|
|
567
553
|
? {
|
|
568
554
|
startRowAbsolute: rowStart?.absolute ?? false,
|
|
569
555
|
endRowAbsolute: rowEnd?.absolute ?? false,
|
|
@@ -575,11 +561,11 @@ function buildParsedRangeReferenceInfo(reference) {
|
|
|
575
561
|
};
|
|
576
562
|
}
|
|
577
563
|
function parseDependencyReference(reference) {
|
|
578
|
-
if (reference.includes(
|
|
564
|
+
if (reference.includes(':')) {
|
|
579
565
|
return buildParsedRangeReferenceInfo(reference);
|
|
580
566
|
}
|
|
581
567
|
return {
|
|
582
|
-
kind:
|
|
568
|
+
kind: 'cell',
|
|
583
569
|
...buildParsedCellReferenceInfo(reference),
|
|
584
570
|
};
|
|
585
571
|
}
|
|
@@ -600,28 +586,28 @@ function buildSimpleCompiledFormula(source) {
|
|
|
600
586
|
const index = refs.push(ref) - 1;
|
|
601
587
|
parsedRefs.push(buildParsedCellReferenceInfo(ref));
|
|
602
588
|
deps.push(ref);
|
|
603
|
-
parsedDeps.push({ kind:
|
|
589
|
+
parsedDeps.push({ kind: 'cell', ...buildParsedCellReferenceInfo(ref) });
|
|
604
590
|
return index;
|
|
605
591
|
};
|
|
606
592
|
const emitOperand = (operand, plan) => {
|
|
607
|
-
if (operand.kind ===
|
|
593
|
+
if (operand.kind === 'CellRef') {
|
|
608
594
|
const index = registerCellRef(operand.ref);
|
|
609
595
|
program.push(encodeInstruction(Opcode.PushCell, index));
|
|
610
|
-
plan.push({ opcode:
|
|
596
|
+
plan.push({ opcode: 'push-cell', address: operand.ref });
|
|
611
597
|
return;
|
|
612
598
|
}
|
|
613
|
-
if (operand.kind ===
|
|
599
|
+
if (operand.kind === 'NumberLiteral') {
|
|
614
600
|
const index = constants.push(operand.value) - 1;
|
|
615
601
|
program.push(encodeInstruction(Opcode.PushNumber, index));
|
|
616
|
-
plan.push({ opcode:
|
|
602
|
+
plan.push({ opcode: 'push-number', value: operand.value });
|
|
617
603
|
return;
|
|
618
604
|
}
|
|
619
605
|
throw new Error(`Unsupported simple operand '${operand.kind}'`);
|
|
620
606
|
};
|
|
621
|
-
if (singleOperand?.kind ===
|
|
607
|
+
if (singleOperand?.kind === 'CellRef') {
|
|
622
608
|
const jsPlan = [];
|
|
623
609
|
emitOperand(singleOperand, jsPlan);
|
|
624
|
-
jsPlan.push({ opcode:
|
|
610
|
+
jsPlan.push({ opcode: 'return' });
|
|
625
611
|
program.push(encodeInstruction(Opcode.Ret));
|
|
626
612
|
return {
|
|
627
613
|
id: 0,
|
|
@@ -660,15 +646,15 @@ function buildSimpleCompiledFormula(source) {
|
|
|
660
646
|
if (!binaryMatch) {
|
|
661
647
|
return null;
|
|
662
648
|
}
|
|
663
|
-
const left = parseSimpleOperand(binaryMatch[1] ??
|
|
649
|
+
const left = parseSimpleOperand(binaryMatch[1] ?? '');
|
|
664
650
|
const operatorCandidate = binaryMatch[2];
|
|
665
|
-
const operator = operatorCandidate ===
|
|
666
|
-
const right = parseSimpleOperand(binaryMatch[3] ??
|
|
651
|
+
const operator = operatorCandidate === '+' || operatorCandidate === '*' ? operatorCandidate : undefined;
|
|
652
|
+
const right = parseSimpleOperand(binaryMatch[3] ?? '');
|
|
667
653
|
if (!left || !right || !operator) {
|
|
668
654
|
return null;
|
|
669
655
|
}
|
|
670
656
|
const ast = {
|
|
671
|
-
kind:
|
|
657
|
+
kind: 'BinaryExpr',
|
|
672
658
|
operator,
|
|
673
659
|
left,
|
|
674
660
|
right,
|
|
@@ -676,9 +662,9 @@ function buildSimpleCompiledFormula(source) {
|
|
|
676
662
|
const jsPlan = [];
|
|
677
663
|
emitOperand(left, jsPlan);
|
|
678
664
|
emitOperand(right, jsPlan);
|
|
679
|
-
jsPlan.push({ opcode:
|
|
680
|
-
jsPlan.push({ opcode:
|
|
681
|
-
program.push(encodeInstruction(operator ===
|
|
665
|
+
jsPlan.push({ opcode: 'binary', operator });
|
|
666
|
+
jsPlan.push({ opcode: 'return' });
|
|
667
|
+
program.push(encodeInstruction(operator === '+' ? Opcode.Add : Opcode.Mul));
|
|
682
668
|
program.push(encodeInstruction(Opcode.Ret));
|
|
683
669
|
return {
|
|
684
670
|
id: 0,
|
|
@@ -714,31 +700,29 @@ function buildSimpleCompiledFormula(source) {
|
|
|
714
700
|
};
|
|
715
701
|
}
|
|
716
702
|
function buildDirectAggregateCandidate(node, symbolicRanges) {
|
|
717
|
-
if (node.kind !==
|
|
703
|
+
if (node.kind !== 'CallExpr' || node.args.length !== 1) {
|
|
718
704
|
return undefined;
|
|
719
705
|
}
|
|
720
706
|
const callee = node.callee.trim().toUpperCase();
|
|
721
|
-
const aggregateKind = callee ===
|
|
722
|
-
?
|
|
723
|
-
: callee ===
|
|
724
|
-
?
|
|
725
|
-
: callee ===
|
|
726
|
-
?
|
|
727
|
-
: callee ===
|
|
728
|
-
?
|
|
729
|
-
: callee ===
|
|
730
|
-
?
|
|
707
|
+
const aggregateKind = callee === 'SUM'
|
|
708
|
+
? 'sum'
|
|
709
|
+
: callee === 'COUNT'
|
|
710
|
+
? 'count'
|
|
711
|
+
: callee === 'MIN'
|
|
712
|
+
? 'min'
|
|
713
|
+
: callee === 'MAX'
|
|
714
|
+
? 'max'
|
|
715
|
+
: callee === 'AVERAGE' || callee === 'AVG'
|
|
716
|
+
? 'average'
|
|
731
717
|
: undefined;
|
|
732
718
|
if (!aggregateKind) {
|
|
733
719
|
return undefined;
|
|
734
720
|
}
|
|
735
721
|
const rangeNode = node.args[0];
|
|
736
|
-
if (!rangeNode || rangeNode.kind !==
|
|
722
|
+
if (!rangeNode || rangeNode.kind !== 'RangeRef' || rangeNode.refKind !== 'cells') {
|
|
737
723
|
return undefined;
|
|
738
724
|
}
|
|
739
|
-
const qualifiedRange = formatRangeAddress(parseRangeAddress(rangeNode.sheetName
|
|
740
|
-
? `${rangeNode.sheetName}!${rangeNode.start}:${rangeNode.end}`
|
|
741
|
-
: `${rangeNode.start}:${rangeNode.end}`));
|
|
725
|
+
const qualifiedRange = formatRangeAddress(parseRangeAddress(rangeNode.sheetName ? `${rangeNode.sheetName}!${rangeNode.start}:${rangeNode.end}` : `${rangeNode.start}:${rangeNode.end}`));
|
|
742
726
|
const symbolicRangeIndex = symbolicRanges.indexOf(qualifiedRange);
|
|
743
727
|
if (symbolicRangeIndex === -1) {
|
|
744
728
|
return undefined;
|