@affino/datagrid-formula-engine 0.1.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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/analysis/index.d.ts +2 -0
  4. package/dist/analysis/index.d.ts.map +1 -0
  5. package/dist/analysis/index.js +1 -0
  6. package/dist/analysis/types.d.ts +44 -0
  7. package/dist/analysis/types.d.ts.map +1 -0
  8. package/dist/analysis/types.js +1 -0
  9. package/dist/contracts.d.ts +177 -0
  10. package/dist/contracts.d.ts.map +1 -0
  11. package/dist/contracts.js +140 -0
  12. package/dist/coreTypes.d.ts +2 -0
  13. package/dist/coreTypes.d.ts.map +1 -0
  14. package/dist/coreTypes.js +1 -0
  15. package/dist/dependency/index.d.ts +2 -0
  16. package/dist/dependency/index.d.ts.map +1 -0
  17. package/dist/dependency/index.js +1 -0
  18. package/dist/evaluators/columnar.d.ts +6 -0
  19. package/dist/evaluators/columnar.d.ts.map +1 -0
  20. package/dist/evaluators/columnar.js +39 -0
  21. package/dist/evaluators/index.d.ts +5 -0
  22. package/dist/evaluators/index.d.ts.map +1 -0
  23. package/dist/evaluators/index.js +4 -0
  24. package/dist/evaluators/interpreter.d.ts +6 -0
  25. package/dist/evaluators/interpreter.d.ts.map +1 -0
  26. package/dist/evaluators/interpreter.js +156 -0
  27. package/dist/evaluators/jit.d.ts +7 -0
  28. package/dist/evaluators/jit.d.ts.map +1 -0
  29. package/dist/evaluators/jit.js +158 -0
  30. package/dist/evaluators/shared.d.ts +36 -0
  31. package/dist/evaluators/shared.d.ts.map +1 -0
  32. package/dist/evaluators/shared.js +242 -0
  33. package/dist/evaluators/vector.d.ts +6 -0
  34. package/dist/evaluators/vector.d.ts.map +1 -0
  35. package/dist/evaluators/vector.js +228 -0
  36. package/dist/formulaEngine/compile.d.ts +2 -0
  37. package/dist/formulaEngine/compile.d.ts.map +1 -0
  38. package/dist/formulaEngine/compile.js +1 -0
  39. package/dist/formulaEngine/core.d.ts +2 -0
  40. package/dist/formulaEngine/core.d.ts.map +1 -0
  41. package/dist/formulaEngine/core.js +1 -0
  42. package/dist/formulaEngine/evaluators.d.ts +2 -0
  43. package/dist/formulaEngine/evaluators.d.ts.map +1 -0
  44. package/dist/formulaEngine/evaluators.js +1 -0
  45. package/dist/formulaEngine/index.d.ts +5 -0
  46. package/dist/formulaEngine/index.d.ts.map +1 -0
  47. package/dist/formulaEngine/index.js +2 -0
  48. package/dist/formulaExecutionPlan.d.ts +2 -0
  49. package/dist/formulaExecutionPlan.d.ts.map +1 -0
  50. package/dist/formulaExecutionPlan.js +1 -0
  51. package/dist/graph/executionPlan.d.ts +66 -0
  52. package/dist/graph/executionPlan.d.ts.map +1 -0
  53. package/dist/graph/executionPlan.js +534 -0
  54. package/dist/graph/index.d.ts +2 -0
  55. package/dist/graph/index.d.ts.map +1 -0
  56. package/dist/graph/index.js +3 -0
  57. package/dist/index.d.ts +8 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +4 -0
  60. package/dist/runtime/compile.d.ts +7 -0
  61. package/dist/runtime/compile.d.ts.map +1 -0
  62. package/dist/runtime/compile.js +453 -0
  63. package/dist/runtime/evaluators.d.ts +5 -0
  64. package/dist/runtime/evaluators.d.ts.map +1 -0
  65. package/dist/runtime/evaluators.js +6 -0
  66. package/dist/runtime/index.d.ts +3 -0
  67. package/dist/runtime/index.d.ts.map +1 -0
  68. package/dist/runtime/index.js +2 -0
  69. package/dist/runtime/types.d.ts +63 -0
  70. package/dist/runtime/types.d.ts.map +1 -0
  71. package/dist/runtime/types.js +1 -0
  72. package/dist/syntax/analysis.d.ts +14 -0
  73. package/dist/syntax/analysis.d.ts.map +1 -0
  74. package/dist/syntax/analysis.js +159 -0
  75. package/dist/syntax/ast.d.ts +18 -0
  76. package/dist/syntax/ast.d.ts.map +1 -0
  77. package/dist/syntax/ast.js +54 -0
  78. package/dist/syntax/core.d.ts +4 -0
  79. package/dist/syntax/core.d.ts.map +1 -0
  80. package/dist/syntax/core.js +1 -0
  81. package/dist/syntax/functionGroups/advancedFunctions.d.ts +2 -0
  82. package/dist/syntax/functionGroups/advancedFunctions.d.ts.map +1 -0
  83. package/dist/syntax/functionGroups/advancedFunctions.js +252 -0
  84. package/dist/syntax/functionGroups/dateFunctions.d.ts +2 -0
  85. package/dist/syntax/functionGroups/dateFunctions.d.ts.map +1 -0
  86. package/dist/syntax/functionGroups/dateFunctions.js +144 -0
  87. package/dist/syntax/functionGroups/logicFunctions.d.ts +2 -0
  88. package/dist/syntax/functionGroups/logicFunctions.d.ts.map +1 -0
  89. package/dist/syntax/functionGroups/logicFunctions.js +140 -0
  90. package/dist/syntax/functionGroups/numericFunctions.d.ts +2 -0
  91. package/dist/syntax/functionGroups/numericFunctions.d.ts.map +1 -0
  92. package/dist/syntax/functionGroups/numericFunctions.js +268 -0
  93. package/dist/syntax/functionGroups/textFunctions.d.ts +2 -0
  94. package/dist/syntax/functionGroups/textFunctions.d.ts.map +1 -0
  95. package/dist/syntax/functionGroups/textFunctions.js +118 -0
  96. package/dist/syntax/functionHelpers.d.ts +45 -0
  97. package/dist/syntax/functionHelpers.d.ts.map +1 -0
  98. package/dist/syntax/functionHelpers.js +553 -0
  99. package/dist/syntax/functions.d.ts +9 -0
  100. package/dist/syntax/functions.d.ts.map +1 -0
  101. package/dist/syntax/functions.js +139 -0
  102. package/dist/syntax/index.d.ts +10 -0
  103. package/dist/syntax/index.d.ts.map +1 -0
  104. package/dist/syntax/index.js +7 -0
  105. package/dist/syntax/legacy.d.ts +29 -0
  106. package/dist/syntax/legacy.d.ts.map +1 -0
  107. package/dist/syntax/legacy.js +188 -0
  108. package/dist/syntax/optimizer.d.ts +6 -0
  109. package/dist/syntax/optimizer.d.ts.map +1 -0
  110. package/dist/syntax/optimizer.js +362 -0
  111. package/dist/syntax/parser.d.ts +7 -0
  112. package/dist/syntax/parser.d.ts.map +1 -0
  113. package/dist/syntax/parser.js +239 -0
  114. package/dist/syntax/tokenizer.d.ts +14 -0
  115. package/dist/syntax/tokenizer.d.ts.map +1 -0
  116. package/dist/syntax/tokenizer.js +852 -0
  117. package/dist/syntax/types.d.ts +120 -0
  118. package/dist/syntax/types.d.ts.map +1 -0
  119. package/dist/syntax/types.js +1 -0
  120. package/dist/syntax/values.d.ts +25 -0
  121. package/dist/syntax/values.d.ts.map +1 -0
  122. package/dist/syntax/values.js +270 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -0
  124. package/package.json +42 -0
@@ -0,0 +1,852 @@
1
+ import { createFormulaSourceSpan, throwFormulaError } from "./ast.js";
2
+ function readEscapedFormulaStringValue(input, start, quote) {
3
+ let cursor = start + 1;
4
+ let value = "";
5
+ while (cursor < input.length) {
6
+ const current = input[cursor];
7
+ if (!current) {
8
+ break;
9
+ }
10
+ if (current === "\\") {
11
+ const escaped = input[cursor + 1];
12
+ if (!escaped) {
13
+ throwFormulaError(`Unterminated string literal at position ${start + 1}.`, createFormulaSourceSpan(start, cursor + 1));
14
+ }
15
+ if (escaped === "n") {
16
+ value += "\n";
17
+ }
18
+ else if (escaped === "r") {
19
+ value += "\r";
20
+ }
21
+ else if (escaped === "t") {
22
+ value += "\t";
23
+ }
24
+ else {
25
+ value += escaped;
26
+ }
27
+ cursor += 2;
28
+ continue;
29
+ }
30
+ if (current === quote) {
31
+ return { value, end: cursor + 1 };
32
+ }
33
+ value += current;
34
+ cursor += 1;
35
+ }
36
+ throwFormulaError(`Unterminated string literal at position ${start + 1}.`, createFormulaSourceSpan(start, cursor));
37
+ }
38
+ function isFormulaReferenceIdentifierStart(character) {
39
+ return typeof character === "string"
40
+ && ((character >= "a" && character <= "z")
41
+ || (character >= "A" && character <= "Z")
42
+ || character === "_"
43
+ || character === "$");
44
+ }
45
+ function isFormulaReferenceIdentifierPart(character) {
46
+ return typeof character === "string"
47
+ && ((character >= "a" && character <= "z")
48
+ || (character >= "A" && character <= "Z")
49
+ || (character >= "0" && character <= "9")
50
+ || character === "_"
51
+ || character === "$");
52
+ }
53
+ function formatFormulaReferenceSegment(segment) {
54
+ if (typeof segment === "number") {
55
+ return String(segment);
56
+ }
57
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(segment)) {
58
+ return segment;
59
+ }
60
+ return `"${segment.replace(/\\/g, "\\\\").replace(/\"/g, '\\\"')}"`;
61
+ }
62
+ function normalizeFormulaReferenceSegments(segments) {
63
+ return segments.map(formatFormulaReferenceSegment).join(".");
64
+ }
65
+ function normalizeFormulaSheetReference(reference) {
66
+ return reference.trim();
67
+ }
68
+ function resolveFormulaReferenceParserOptions(options) {
69
+ var _a;
70
+ const syntax = (_a = options === null || options === void 0 ? void 0 : options.syntax) !== null && _a !== void 0 ? _a : "canonical";
71
+ return {
72
+ syntax,
73
+ smartsheetAbsoluteRowBase: (options === null || options === void 0 ? void 0 : options.smartsheetAbsoluteRowBase) === 0 ? 0 : 1,
74
+ allowSheetQualifiedReferences: (options === null || options === void 0 ? void 0 : options.allowSheetQualifiedReferences) === true,
75
+ };
76
+ }
77
+ function supportsSmartsheetFormulaReferenceSyntax(syntax) {
78
+ return syntax === "smartsheet" || syntax === "auto";
79
+ }
80
+ function formatFormulaSheetReference(reference) {
81
+ const normalized = normalizeFormulaSheetReference(reference);
82
+ if (/^[A-Za-z_$][A-Za-z0-9_$.-]*$/.test(normalized)) {
83
+ return normalized;
84
+ }
85
+ return `'${normalized.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
86
+ }
87
+ function serializeParsedFormulaIdentifier(sheetReference, referenceName, rangeReferenceName, rowSelector) {
88
+ const prefix = sheetReference ? `${formatFormulaSheetReference(sheetReference)}!` : "";
89
+ const localReference = rangeReferenceName && rangeReferenceName !== referenceName
90
+ ? `${referenceName}:${rangeReferenceName}`
91
+ : referenceName;
92
+ return `${prefix}${localReference}${serializeFormulaRowSelector(rowSelector)}`;
93
+ }
94
+ export function normalizeFormulaReference(reference) {
95
+ const normalized = reference.trim();
96
+ if (normalized.length === 0) {
97
+ return "";
98
+ }
99
+ const parsed = readFormulaReferenceAt(normalized, 0);
100
+ if (!parsed || parsed.end !== normalized.length) {
101
+ return normalized;
102
+ }
103
+ return parsed.value;
104
+ }
105
+ function looksLikeFormulaRowSelectorValue(value) {
106
+ const normalized = value.trim();
107
+ return /^[+-]?\d+$/.test(normalized)
108
+ || /^\d+\s*\.\.\s*\d+$/.test(normalized)
109
+ || /^[+-]?\d+\s*:\s*[+-]?\d+$/.test(normalized);
110
+ }
111
+ function parseFormulaRowSelectorValue(value) {
112
+ const normalized = value.trim();
113
+ const absoluteWindowMatch = /^(\d+)\s*\.\.\s*(\d+)$/.exec(normalized);
114
+ if (absoluteWindowMatch) {
115
+ const startRowIndex = Number(absoluteWindowMatch[1]);
116
+ const endRowIndex = Number(absoluteWindowMatch[2]);
117
+ if (startRowIndex > endRowIndex) {
118
+ return null;
119
+ }
120
+ return {
121
+ kind: "absolute-window",
122
+ startRowIndex,
123
+ endRowIndex,
124
+ };
125
+ }
126
+ const windowMatch = /^([+-]?\d+)\s*:\s*([+-]?\d+)$/.exec(normalized);
127
+ if (windowMatch) {
128
+ const startOffset = Number(windowMatch[1]);
129
+ const endOffset = Number(windowMatch[2]);
130
+ if (startOffset > endOffset) {
131
+ return null;
132
+ }
133
+ return {
134
+ kind: "window",
135
+ startOffset,
136
+ endOffset,
137
+ };
138
+ }
139
+ if (/^[+-]\d+$/.test(normalized)) {
140
+ return { kind: "relative", offset: Number(normalized) };
141
+ }
142
+ if (/^\d+$/.test(normalized)) {
143
+ return { kind: "absolute", rowIndex: Number(normalized) };
144
+ }
145
+ return null;
146
+ }
147
+ function serializeFormulaRowSelector(selector) {
148
+ if (selector.kind === "current") {
149
+ return "";
150
+ }
151
+ if (selector.kind === "absolute") {
152
+ return `[${selector.rowIndex}]`;
153
+ }
154
+ if (selector.kind === "absolute-window") {
155
+ return `[${selector.startRowIndex}..${selector.endRowIndex}]`;
156
+ }
157
+ if (selector.kind === "relative") {
158
+ return `[${selector.offset >= 0 ? `+${selector.offset}` : selector.offset}]`;
159
+ }
160
+ const startOffset = selector.startOffset >= 0 ? `+${selector.startOffset}` : String(selector.startOffset);
161
+ const endOffset = selector.endOffset >= 0 ? `+${selector.endOffset}` : String(selector.endOffset);
162
+ return `[${startOffset}:${endOffset}]`;
163
+ }
164
+ function readFormulaReferenceAt(input, start) {
165
+ let cursor = start;
166
+ const segments = [];
167
+ let expectSegment = true;
168
+ const pushSimpleSegment = () => {
169
+ if (!isFormulaReferenceIdentifierStart(input[cursor])) {
170
+ return false;
171
+ }
172
+ const segmentStart = cursor;
173
+ cursor += 1;
174
+ while (cursor < input.length && isFormulaReferenceIdentifierPart(input[cursor])) {
175
+ cursor += 1;
176
+ }
177
+ segments.push(input.slice(segmentStart, cursor));
178
+ expectSegment = false;
179
+ return true;
180
+ };
181
+ const pushNumericSegment = () => {
182
+ const current = input[cursor];
183
+ if (!(current && current >= "0" && current <= "9")) {
184
+ return false;
185
+ }
186
+ const segmentStart = cursor;
187
+ cursor += 1;
188
+ while (cursor < input.length) {
189
+ const next = input[cursor];
190
+ if (!(next && next >= "0" && next <= "9")) {
191
+ break;
192
+ }
193
+ cursor += 1;
194
+ }
195
+ const parsedIndex = Number(input.slice(segmentStart, cursor));
196
+ segments.push(parsedIndex);
197
+ expectSegment = false;
198
+ return true;
199
+ };
200
+ const pushQuotedSegment = () => {
201
+ const current = input[cursor];
202
+ if (current !== "'" && current !== '"') {
203
+ return false;
204
+ }
205
+ const parsed = readEscapedFormulaStringValue(input, cursor, current);
206
+ segments.push(parsed.value);
207
+ cursor = parsed.end;
208
+ expectSegment = false;
209
+ return true;
210
+ };
211
+ const pushBracketSegment = () => {
212
+ var _a, _b;
213
+ if (input[cursor] !== "[") {
214
+ return false;
215
+ }
216
+ const bracketStart = cursor;
217
+ cursor += 1;
218
+ while (cursor < input.length && /\s/.test((_a = input[cursor]) !== null && _a !== void 0 ? _a : "")) {
219
+ cursor += 1;
220
+ }
221
+ if (cursor >= input.length) {
222
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
223
+ }
224
+ let segmentValue;
225
+ const current = input[cursor];
226
+ if (current === "'" || current === '"') {
227
+ const parsed = readEscapedFormulaStringValue(input, cursor, current);
228
+ segmentValue = parsed.value;
229
+ cursor = parsed.end;
230
+ while (cursor < input.length && /\s/.test((_b = input[cursor]) !== null && _b !== void 0 ? _b : "")) {
231
+ cursor += 1;
232
+ }
233
+ if (input[cursor] !== "]") {
234
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
235
+ }
236
+ }
237
+ else {
238
+ const valueStart = cursor;
239
+ while (cursor < input.length && input[cursor] !== "]") {
240
+ cursor += 1;
241
+ }
242
+ if (input[cursor] !== "]") {
243
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
244
+ }
245
+ segmentValue = input.slice(valueStart, cursor).trim();
246
+ if (segmentValue.length === 0) {
247
+ throwFormulaError(`Empty bracket reference at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor + 1));
248
+ }
249
+ }
250
+ const parsedIndex = Number(segmentValue);
251
+ if (String(parsedIndex) === segmentValue && Number.isInteger(parsedIndex) && parsedIndex >= 0) {
252
+ segments.push(parsedIndex);
253
+ }
254
+ else {
255
+ segments.push(segmentValue);
256
+ }
257
+ cursor += 1;
258
+ expectSegment = false;
259
+ return true;
260
+ };
261
+ while (cursor < input.length) {
262
+ if (expectSegment) {
263
+ if (pushSimpleSegment() || pushNumericSegment() || pushQuotedSegment() || pushBracketSegment()) {
264
+ continue;
265
+ }
266
+ break;
267
+ }
268
+ const current = input[cursor];
269
+ if (current === ".") {
270
+ cursor += 1;
271
+ expectSegment = true;
272
+ continue;
273
+ }
274
+ if (current === "[") {
275
+ expectSegment = true;
276
+ continue;
277
+ }
278
+ break;
279
+ }
280
+ if (segments.length === 0) {
281
+ return null;
282
+ }
283
+ if (expectSegment) {
284
+ throwFormulaError(`Incomplete reference at position ${cursor + 1}.`, createFormulaSourceSpan(start, Math.max(start + 1, cursor)));
285
+ }
286
+ return {
287
+ value: normalizeFormulaReferenceSegments(segments),
288
+ end: cursor,
289
+ };
290
+ }
291
+ function readFormulaReferenceNameAt(input, start) {
292
+ const parsed = readFormulaReferenceAt(input, start);
293
+ if (!parsed) {
294
+ return null;
295
+ }
296
+ return {
297
+ referenceName: parsed.value,
298
+ end: parsed.end,
299
+ };
300
+ }
301
+ function isFormulaReferenceBoundary(character) {
302
+ return typeof character !== "string"
303
+ || character === " "
304
+ || character === "\t"
305
+ || character === "\n"
306
+ || character === "\r"
307
+ || character === "+"
308
+ || character === "-"
309
+ || character === "*"
310
+ || character === "/"
311
+ || character === ","
312
+ || character === "("
313
+ || character === ")"
314
+ || character === ">"
315
+ || character === "<"
316
+ || character === "="
317
+ || character === "!";
318
+ }
319
+ function readCanonicalTrailingFormulaRowSelectorAt(input, start) {
320
+ if (input[start] !== "[") {
321
+ return null;
322
+ }
323
+ const bracketStart = start;
324
+ let cursor = start + 1;
325
+ while (cursor < input.length && input[cursor] !== "]") {
326
+ cursor += 1;
327
+ }
328
+ if (input[cursor] !== "]") {
329
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
330
+ }
331
+ const selectorValue = input.slice(start + 1, cursor).trim();
332
+ const rowSelector = parseFormulaRowSelectorValue(selectorValue);
333
+ if (rowSelector) {
334
+ const end = cursor + 1;
335
+ if (!isFormulaReferenceBoundary(input[end])) {
336
+ return null;
337
+ }
338
+ return {
339
+ rowSelector,
340
+ end,
341
+ };
342
+ }
343
+ if (looksLikeFormulaRowSelectorValue(selectorValue) && isFormulaReferenceBoundary(input[cursor + 1])) {
344
+ throwFormulaError(`Invalid row selector '${selectorValue}' in reference '${input.slice(0, cursor + 1).trim()}'.`, createFormulaSourceSpan(bracketStart, cursor + 1));
345
+ }
346
+ return null;
347
+ }
348
+ function readSmartsheetTrailingFormulaRowSelectorAt(input, start, referenceName, options) {
349
+ var _a;
350
+ const current = input[start];
351
+ if (current === "@") {
352
+ const keyword = input.slice(start, start + 4).toLowerCase();
353
+ if (keyword !== "@row") {
354
+ return null;
355
+ }
356
+ return {
357
+ rangeReferenceName: null,
358
+ rowSelector: { kind: "current" },
359
+ end: start + 4,
360
+ };
361
+ }
362
+ const readAbsoluteRowSelector = (selectorStart) => {
363
+ const selectorCurrent = input[selectorStart];
364
+ if (!(selectorCurrent && selectorCurrent >= "0" && selectorCurrent <= "9")) {
365
+ return null;
366
+ }
367
+ let selectorCursor = selectorStart + 1;
368
+ while (selectorCursor < input.length) {
369
+ const next = input[selectorCursor];
370
+ if (!(next && next >= "0" && next <= "9")) {
371
+ break;
372
+ }
373
+ selectorCursor += 1;
374
+ }
375
+ const rowNumber = Number(input.slice(selectorStart, selectorCursor));
376
+ if (!Number.isInteger(rowNumber)) {
377
+ return null;
378
+ }
379
+ if (options.smartsheetAbsoluteRowBase === 1 && rowNumber <= 0) {
380
+ throwFormulaError(`Smartsheet row selector '${input.slice(selectorStart, selectorCursor)}' must be >= 1.`, createFormulaSourceSpan(selectorStart, selectorCursor));
381
+ }
382
+ return {
383
+ rowIndex: rowNumber - options.smartsheetAbsoluteRowBase,
384
+ end: selectorCursor,
385
+ };
386
+ };
387
+ const absoluteSelector = readAbsoluteRowSelector(start);
388
+ if (!absoluteSelector) {
389
+ return null;
390
+ }
391
+ let cursor = absoluteSelector.end;
392
+ if (input[cursor] !== ":") {
393
+ return {
394
+ rangeReferenceName: null,
395
+ rowSelector: {
396
+ kind: "absolute",
397
+ rowIndex: absoluteSelector.rowIndex,
398
+ },
399
+ end: absoluteSelector.end,
400
+ };
401
+ }
402
+ cursor += 1;
403
+ while (cursor < input.length && /\s/.test((_a = input[cursor]) !== null && _a !== void 0 ? _a : "")) {
404
+ cursor += 1;
405
+ }
406
+ let rangeReferenceName = null;
407
+ if (input[cursor] === "[") {
408
+ const rangeReference = readFormulaReferenceNameAt(input, cursor);
409
+ if (!rangeReference) {
410
+ throwFormulaError(`Invalid Smartsheet range reference '${input.slice(start, cursor + 1)}'.`, createFormulaSourceSpan(start, cursor + 1));
411
+ }
412
+ rangeReferenceName = rangeReference.referenceName === referenceName ? null : rangeReference.referenceName;
413
+ cursor = rangeReference.end;
414
+ }
415
+ const rangeEndSelector = readAbsoluteRowSelector(cursor);
416
+ if (!rangeEndSelector) {
417
+ throwFormulaError(`Invalid Smartsheet range reference '${input.slice(start, Math.max(start + 1, cursor + 1))}'.`, createFormulaSourceSpan(start, Math.max(start + 1, cursor + 1)));
418
+ }
419
+ if (rangeEndSelector.rowIndex < absoluteSelector.rowIndex) {
420
+ throwFormulaError(`Smartsheet range start must be <= range end.`, createFormulaSourceSpan(start, rangeEndSelector.end));
421
+ }
422
+ return {
423
+ rangeReferenceName,
424
+ rowSelector: {
425
+ kind: "absolute-window",
426
+ startRowIndex: absoluteSelector.rowIndex,
427
+ endRowIndex: rangeEndSelector.rowIndex,
428
+ },
429
+ end: rangeEndSelector.end,
430
+ };
431
+ }
432
+ function readLocalFormulaIdentifierAt(input, start, options) {
433
+ const parserOptions = resolveFormulaReferenceParserOptions(options);
434
+ let cursor = start;
435
+ const segments = [];
436
+ let expectSegment = true;
437
+ const pushSimpleSegment = () => {
438
+ if (!isFormulaReferenceIdentifierStart(input[cursor])) {
439
+ return false;
440
+ }
441
+ const segmentStart = cursor;
442
+ cursor += 1;
443
+ while (cursor < input.length && isFormulaReferenceIdentifierPart(input[cursor])) {
444
+ cursor += 1;
445
+ }
446
+ segments.push(input.slice(segmentStart, cursor));
447
+ expectSegment = false;
448
+ return true;
449
+ };
450
+ const pushNumericSegment = () => {
451
+ const current = input[cursor];
452
+ if (!(current && current >= "0" && current <= "9")) {
453
+ return false;
454
+ }
455
+ const segmentStart = cursor;
456
+ cursor += 1;
457
+ while (cursor < input.length) {
458
+ const next = input[cursor];
459
+ if (!(next && next >= "0" && next <= "9")) {
460
+ break;
461
+ }
462
+ cursor += 1;
463
+ }
464
+ const parsedIndex = Number(input.slice(segmentStart, cursor));
465
+ segments.push(parsedIndex);
466
+ expectSegment = false;
467
+ return true;
468
+ };
469
+ const pushQuotedSegment = () => {
470
+ const current = input[cursor];
471
+ if (current !== "'" && current !== '"') {
472
+ return false;
473
+ }
474
+ const parsed = readEscapedFormulaStringValue(input, cursor, current);
475
+ segments.push(parsed.value);
476
+ cursor = parsed.end;
477
+ expectSegment = false;
478
+ return true;
479
+ };
480
+ const pushBracketSegment = () => {
481
+ var _a, _b;
482
+ if (input[cursor] !== "[") {
483
+ return false;
484
+ }
485
+ const bracketStart = cursor;
486
+ cursor += 1;
487
+ while (cursor < input.length && /\s/.test((_a = input[cursor]) !== null && _a !== void 0 ? _a : "")) {
488
+ cursor += 1;
489
+ }
490
+ if (cursor >= input.length) {
491
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
492
+ }
493
+ let segmentValue;
494
+ const current = input[cursor];
495
+ if (current === "'" || current === '"') {
496
+ const parsed = readEscapedFormulaStringValue(input, cursor, current);
497
+ segmentValue = parsed.value;
498
+ cursor = parsed.end;
499
+ while (cursor < input.length && /\s/.test((_b = input[cursor]) !== null && _b !== void 0 ? _b : "")) {
500
+ cursor += 1;
501
+ }
502
+ if (input[cursor] !== "]") {
503
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
504
+ }
505
+ }
506
+ else {
507
+ const valueStart = cursor;
508
+ while (cursor < input.length && input[cursor] !== "]") {
509
+ cursor += 1;
510
+ }
511
+ if (input[cursor] !== "]") {
512
+ throwFormulaError(`Missing ']' for reference starting at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor));
513
+ }
514
+ segmentValue = input.slice(valueStart, cursor).trim();
515
+ if (segmentValue.length === 0) {
516
+ throwFormulaError(`Empty bracket reference at position ${bracketStart + 1}.`, createFormulaSourceSpan(bracketStart, cursor + 1));
517
+ }
518
+ }
519
+ const parsedIndex = Number(segmentValue);
520
+ if (String(parsedIndex) === segmentValue && Number.isInteger(parsedIndex) && parsedIndex >= 0) {
521
+ segments.push(parsedIndex);
522
+ }
523
+ else {
524
+ segments.push(segmentValue);
525
+ }
526
+ cursor += 1;
527
+ expectSegment = false;
528
+ return true;
529
+ };
530
+ while (cursor < input.length) {
531
+ if (expectSegment) {
532
+ if (pushSimpleSegment() || pushNumericSegment() || pushQuotedSegment() || pushBracketSegment()) {
533
+ continue;
534
+ }
535
+ break;
536
+ }
537
+ const current = input[cursor];
538
+ if (current === ".") {
539
+ cursor += 1;
540
+ expectSegment = true;
541
+ continue;
542
+ }
543
+ if (current === "[") {
544
+ if (segments.length > 0) {
545
+ const rowSelector = readCanonicalTrailingFormulaRowSelectorAt(input, cursor);
546
+ if (rowSelector) {
547
+ const referenceName = normalizeFormulaReferenceSegments(segments);
548
+ return {
549
+ value: serializeParsedFormulaIdentifier(null, referenceName, null, rowSelector.rowSelector),
550
+ sheetReference: null,
551
+ referenceName,
552
+ rangeReferenceName: null,
553
+ rowSelector: rowSelector.rowSelector,
554
+ end: rowSelector.end,
555
+ };
556
+ }
557
+ }
558
+ expectSegment = true;
559
+ continue;
560
+ }
561
+ break;
562
+ }
563
+ if (segments.length === 0) {
564
+ return null;
565
+ }
566
+ if (expectSegment) {
567
+ throwFormulaError(`Incomplete reference at position ${cursor + 1}.`, createFormulaSourceSpan(start, Math.max(start + 1, cursor)));
568
+ }
569
+ const referenceName = normalizeFormulaReferenceSegments(segments);
570
+ if (input[cursor] === ":") {
571
+ const rangeReference = readLocalFormulaIdentifierAt(input, cursor + 1, parserOptions);
572
+ if (!rangeReference) {
573
+ throwFormulaError(`Incomplete range reference at position ${cursor + 1}.`, createFormulaSourceSpan(start, Math.max(start + 1, cursor + 1)));
574
+ }
575
+ if (rangeReference.rangeReferenceName) {
576
+ throwFormulaError(`Nested range references are not supported.`, createFormulaSourceSpan(start, rangeReference.end));
577
+ }
578
+ return {
579
+ value: serializeParsedFormulaIdentifier(null, referenceName, rangeReference.referenceName, rangeReference.rowSelector),
580
+ sheetReference: null,
581
+ referenceName,
582
+ rangeReferenceName: rangeReference.referenceName,
583
+ rowSelector: rangeReference.rowSelector,
584
+ end: rangeReference.end,
585
+ };
586
+ }
587
+ if (input[cursor] === "[") {
588
+ const rowSelector = readCanonicalTrailingFormulaRowSelectorAt(input, cursor);
589
+ if (rowSelector) {
590
+ return {
591
+ value: serializeParsedFormulaIdentifier(null, referenceName, null, rowSelector.rowSelector),
592
+ sheetReference: null,
593
+ referenceName,
594
+ rangeReferenceName: null,
595
+ rowSelector: rowSelector.rowSelector,
596
+ end: rowSelector.end,
597
+ };
598
+ }
599
+ }
600
+ if (supportsSmartsheetFormulaReferenceSyntax(parserOptions.syntax)) {
601
+ const smartsheetSelector = readSmartsheetTrailingFormulaRowSelectorAt(input, cursor, referenceName, parserOptions);
602
+ if (smartsheetSelector && isFormulaReferenceBoundary(input[smartsheetSelector.end])) {
603
+ return {
604
+ value: serializeParsedFormulaIdentifier(null, referenceName, smartsheetSelector.rangeReferenceName, smartsheetSelector.rowSelector),
605
+ sheetReference: null,
606
+ referenceName,
607
+ rangeReferenceName: smartsheetSelector.rangeReferenceName,
608
+ rowSelector: smartsheetSelector.rowSelector,
609
+ end: smartsheetSelector.end,
610
+ };
611
+ }
612
+ }
613
+ return {
614
+ value: referenceName,
615
+ sheetReference: null,
616
+ referenceName,
617
+ rangeReferenceName: null,
618
+ rowSelector: { kind: "current" },
619
+ end: cursor,
620
+ };
621
+ }
622
+ function readFormulaSheetReferenceAt(input, start) {
623
+ const current = input[start];
624
+ if (!current) {
625
+ return null;
626
+ }
627
+ if (current === "'" || current === '"') {
628
+ const parsed = readEscapedFormulaStringValue(input, start, current);
629
+ const sheetReference = normalizeFormulaSheetReference(parsed.value);
630
+ return sheetReference.length === 0
631
+ ? null
632
+ : {
633
+ sheetReference,
634
+ end: parsed.end,
635
+ };
636
+ }
637
+ if (!isFormulaReferenceIdentifierStart(current)) {
638
+ return null;
639
+ }
640
+ let cursor = start + 1;
641
+ while (cursor < input.length) {
642
+ const next = input[cursor];
643
+ if (!next
644
+ || !(isFormulaReferenceIdentifierPart(next)
645
+ || next === "-"
646
+ || next === ".")) {
647
+ break;
648
+ }
649
+ cursor += 1;
650
+ }
651
+ const sheetReference = normalizeFormulaSheetReference(input.slice(start, cursor));
652
+ return sheetReference.length === 0
653
+ ? null
654
+ : {
655
+ sheetReference,
656
+ end: cursor,
657
+ };
658
+ }
659
+ function readFormulaIdentifierAt(input, start, options) {
660
+ const parserOptions = resolveFormulaReferenceParserOptions(options);
661
+ if (parserOptions.allowSheetQualifiedReferences) {
662
+ const sheetReference = readFormulaSheetReferenceAt(input, start);
663
+ if (sheetReference && input[sheetReference.end] === "!") {
664
+ const nested = readLocalFormulaIdentifierAt(input, sheetReference.end + 1, parserOptions);
665
+ if (nested) {
666
+ return {
667
+ value: serializeParsedFormulaIdentifier(sheetReference.sheetReference, nested.referenceName, nested.rangeReferenceName, nested.rowSelector),
668
+ sheetReference: sheetReference.sheetReference,
669
+ referenceName: nested.referenceName,
670
+ rangeReferenceName: nested.rangeReferenceName,
671
+ rowSelector: nested.rowSelector,
672
+ end: nested.end,
673
+ };
674
+ }
675
+ }
676
+ }
677
+ return readLocalFormulaIdentifierAt(input, start, parserOptions);
678
+ }
679
+ export function parseDataGridFormulaIdentifier(reference, options = {}) {
680
+ const normalizedReference = reference.trim();
681
+ if (normalizedReference.length === 0) {
682
+ return {
683
+ name: "",
684
+ sheetReference: null,
685
+ referenceName: "",
686
+ rangeReferenceName: null,
687
+ rowSelector: { kind: "current" },
688
+ };
689
+ }
690
+ const parsed = readFormulaIdentifierAt(normalizedReference, 0, options);
691
+ if (parsed && parsed.end === normalizedReference.length) {
692
+ return {
693
+ name: parsed.value,
694
+ sheetReference: parsed.sheetReference,
695
+ referenceName: parsed.referenceName,
696
+ rangeReferenceName: parsed.rangeReferenceName,
697
+ rowSelector: parsed.rowSelector,
698
+ };
699
+ }
700
+ const referenceName = normalizeFormulaReference(normalizedReference);
701
+ return {
702
+ name: referenceName,
703
+ sheetReference: null,
704
+ referenceName,
705
+ rangeReferenceName: null,
706
+ rowSelector: { kind: "current" },
707
+ };
708
+ }
709
+ export function parseFormulaReferenceSegments(reference) {
710
+ const normalized = normalizeFormulaReference(reference);
711
+ if (normalized.length === 0) {
712
+ return [];
713
+ }
714
+ const parsed = readFormulaReferenceAt(normalized, 0);
715
+ if (!parsed || parsed.end !== normalized.length) {
716
+ return [normalized];
717
+ }
718
+ return Object.freeze(parsed.value
719
+ .split(/\.(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/)
720
+ .filter(segment => segment.length > 0)
721
+ .map((segment) => {
722
+ const trimmed = segment.trim();
723
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
724
+ return readEscapedFormulaStringValue(trimmed, 0, '"').value;
725
+ }
726
+ const parsedIndex = Number(trimmed);
727
+ return String(parsedIndex) === trimmed && Number.isInteger(parsedIndex) && parsedIndex >= 0
728
+ ? parsedIndex
729
+ : trimmed;
730
+ }));
731
+ }
732
+ export function tokenizeFormula(formula, options = {}) {
733
+ const tokens = [];
734
+ const parserOptions = resolveFormulaReferenceParserOptions(options);
735
+ let cursor = 0;
736
+ const isDigitAt = (index) => {
737
+ const character = formula[index];
738
+ return typeof character === "string" && character >= "0" && character <= "9";
739
+ };
740
+ const pushNumber = () => {
741
+ const start = cursor;
742
+ let hasDigits = false;
743
+ while (cursor < formula.length && isDigitAt(cursor)) {
744
+ cursor += 1;
745
+ hasDigits = true;
746
+ }
747
+ if (formula[cursor] === ".") {
748
+ cursor += 1;
749
+ while (cursor < formula.length && isDigitAt(cursor)) {
750
+ cursor += 1;
751
+ hasDigits = true;
752
+ }
753
+ }
754
+ if (!hasDigits) {
755
+ throwFormulaError(`Invalid number at position ${start + 1}.`);
756
+ }
757
+ const raw = formula.slice(start, cursor);
758
+ const parsed = Number(raw);
759
+ if (!Number.isFinite(parsed)) {
760
+ throwFormulaError(`Invalid number '${raw}' at position ${start + 1}.`, createFormulaSourceSpan(start, cursor));
761
+ }
762
+ tokens.push({ kind: "number", value: parsed, position: start, end: cursor });
763
+ };
764
+ const pushString = (quote) => {
765
+ const start = cursor;
766
+ const parsed = readEscapedFormulaStringValue(formula, start, quote);
767
+ cursor = parsed.end;
768
+ tokens.push({ kind: "string", value: parsed.value, position: start, end: cursor });
769
+ };
770
+ const pushIdentifier = () => {
771
+ const start = cursor;
772
+ const parsed = readFormulaIdentifierAt(formula, cursor, parserOptions);
773
+ if (!parsed) {
774
+ throwFormulaError(`Unexpected token '${formula[cursor]}' at position ${cursor + 1}.`, createFormulaSourceSpan(cursor, cursor + 1));
775
+ }
776
+ cursor = parsed.end;
777
+ const value = parsed.value;
778
+ const keyword = value.toUpperCase();
779
+ if (keyword === "AND" || keyword === "OR" || keyword === "NOT") {
780
+ tokens.push({ kind: "operator", value: keyword, position: start, end: cursor });
781
+ return;
782
+ }
783
+ tokens.push({ kind: "identifier", value, raw: formula.slice(start, cursor), position: start, end: cursor });
784
+ };
785
+ while (cursor < formula.length) {
786
+ const current = formula[cursor];
787
+ if (!current) {
788
+ break;
789
+ }
790
+ if (current === " " || current === "\t" || current === "\n" || current === "\r") {
791
+ cursor += 1;
792
+ continue;
793
+ }
794
+ if ((current === "'" || current === '"')
795
+ && parserOptions.allowSheetQualifiedReferences) {
796
+ const sheetReference = readFormulaSheetReferenceAt(formula, cursor);
797
+ if (sheetReference && formula[sheetReference.end] === "!" && readLocalFormulaIdentifierAt(formula, sheetReference.end + 1, parserOptions)) {
798
+ pushIdentifier();
799
+ continue;
800
+ }
801
+ }
802
+ if (current === "'" || current === '"') {
803
+ pushString(current);
804
+ continue;
805
+ }
806
+ if ((current >= "0" && current <= "9") || current === ".") {
807
+ pushNumber();
808
+ continue;
809
+ }
810
+ const isIdentifierStart = isFormulaReferenceIdentifierStart(current) || current === "[";
811
+ if (isIdentifierStart) {
812
+ pushIdentifier();
813
+ continue;
814
+ }
815
+ const next = formula[cursor + 1];
816
+ if ((current === ">" || current === "<" || current === "=" || current === "!") && next === "=") {
817
+ tokens.push({ kind: "operator", value: `${current}${next}`, position: cursor, end: cursor + 2 });
818
+ cursor += 2;
819
+ continue;
820
+ }
821
+ if (current === ">" || current === "<") {
822
+ tokens.push({ kind: "operator", value: current, position: cursor, end: cursor + 1 });
823
+ cursor += 1;
824
+ continue;
825
+ }
826
+ if (current === "+" || current === "-" || current === "*" || current === "/") {
827
+ tokens.push({ kind: "operator", value: current, position: cursor, end: cursor + 1 });
828
+ cursor += 1;
829
+ continue;
830
+ }
831
+ if (current === "(") {
832
+ tokens.push({ kind: "paren", value: "(", position: cursor, end: cursor + 1 });
833
+ cursor += 1;
834
+ continue;
835
+ }
836
+ if (current === ")") {
837
+ tokens.push({ kind: "paren", value: ")", position: cursor, end: cursor + 1 });
838
+ cursor += 1;
839
+ continue;
840
+ }
841
+ if (current === ",") {
842
+ tokens.push({ kind: "comma", position: cursor, end: cursor + 1 });
843
+ cursor += 1;
844
+ continue;
845
+ }
846
+ throwFormulaError(`Unexpected token '${current}' at position ${cursor + 1}.`, createFormulaSourceSpan(cursor, cursor + 1));
847
+ }
848
+ if (tokens.length === 0) {
849
+ throwFormulaError("Formula has no expression.");
850
+ }
851
+ return tokens;
852
+ }