@bilig/formula 0.1.74 → 0.1.76

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.
@@ -1,4 +1,6 @@
1
- import { ErrorCode } from "@bilig/protocol";
1
+ import { ErrorCode, FormulaMode } from "@bilig/protocol";
2
+ import { formatRangeAddress, parseCellAddress, parseRangeAddress } from "./addressing.js";
3
+ import { compileFormulaAst, } from "./compiler.js";
2
4
  import { parseFormula } from "./parser.js";
3
5
  const CELL_REF_RE = /^(\$?)([A-Z]+)(\$?)([1-9][0-9]*)$/;
4
6
  const COLUMN_REF_RE = /^(\$?)([A-Z]+)$/;
@@ -34,10 +36,141 @@ export function translateFormulaReferences(source, rowDelta, colDelta) {
34
36
  const ast = parseFormula(source);
35
37
  return serializeFormula(translateNode(ast, rowDelta, colDelta));
36
38
  }
39
+ export function buildRelativeFormulaTemplateKey(source, ownerRow, ownerCol) {
40
+ return buildRelativeFormulaTemplateKeyFromAst(parseFormula(source), ownerRow, ownerCol);
41
+ }
42
+ export function buildRelativeFormulaTemplateKeyFromAst(node, ownerRow, ownerCol) {
43
+ return buildRelativeFormulaTemplateKeyInternal(node, ownerRow, ownerCol);
44
+ }
45
+ export function canTranslateCompiledFormulaWithoutAst(compiled) {
46
+ return ((compiled.symbolicRanges.length === 0 || compiled.directAggregateCandidate !== undefined) &&
47
+ compiled.symbolicNames.length === 0 &&
48
+ compiled.symbolicTables.length === 0 &&
49
+ compiled.symbolicSpills.length === 0 &&
50
+ !compiled.jsPlan.some((instruction) => instruction.opcode === "lookup-exact-match" ||
51
+ instruction.opcode === "lookup-approximate-match"));
52
+ }
53
+ export function translateCompiledFormulaWithoutAst(compiled, rowDelta, colDelta, sourceOverride) {
54
+ const translatedParsedDeps = compiled.parsedDeps?.map((dependency) => translateParsedDependencyReference(dependency, rowDelta, colDelta));
55
+ const translatedParsedSymbolicRefs = compiled.parsedSymbolicRefs?.map((reference) => translateParsedCellReference(reference, rowDelta, colDelta));
56
+ const translatedParsedSymbolicRanges = compiled.parsedSymbolicRanges?.map((range) => translateParsedRangeReference(range, rowDelta, colDelta));
57
+ const source = sourceOverride ?? compiled.source;
58
+ const translatedCellMap = buildTranslatedCellReferenceMap(compiled.parsedSymbolicRefs, translatedParsedSymbolicRefs);
59
+ const translatedRangeMap = buildTranslatedRangeReferenceMap(compiled.parsedSymbolicRanges, translatedParsedSymbolicRanges);
60
+ return {
61
+ source,
62
+ compiled: {
63
+ ...compiled,
64
+ source,
65
+ astMatchesSource: false,
66
+ deps: translatedParsedDeps?.map((dependency) => formatParsedDependencyReference(dependency)) ??
67
+ compiled.deps.map((dependency) => translateQualifiedDependencyReference(dependency, rowDelta, colDelta)),
68
+ symbolicRefs: translatedParsedSymbolicRefs?.map((reference) => formatParsedCellReference(reference)) ??
69
+ compiled.symbolicRefs.map((ref) => translateQualifiedCellReference(ref, rowDelta, colDelta)),
70
+ symbolicRanges: translatedParsedSymbolicRanges?.map((range) => formatParsedRangeReference(range)) ??
71
+ compiled.symbolicRanges.map((range) => translateQualifiedRangeReference(range, rowDelta, colDelta)),
72
+ jsPlan: compiled.symbolicRanges.length === 0 && compiled.mode === FormulaMode.WasmFastPath
73
+ ? compiled.jsPlan
74
+ : compiled.jsPlan.map((instruction) => translateJsPlanInstructionWithoutAst(instruction, translatedCellMap, translatedRangeMap, rowDelta, colDelta)),
75
+ ...(translatedParsedDeps ? { parsedDeps: translatedParsedDeps } : {}),
76
+ ...(translatedParsedSymbolicRefs ? { parsedSymbolicRefs: translatedParsedSymbolicRefs } : {}),
77
+ ...(translatedParsedSymbolicRanges
78
+ ? { parsedSymbolicRanges: translatedParsedSymbolicRanges }
79
+ : {}),
80
+ },
81
+ };
82
+ }
83
+ export function translateCompiledFormula(compiled, rowDelta, colDelta, sourceOverride) {
84
+ const translatedAst = translateNode(compiled.ast, rowDelta, colDelta);
85
+ const translatedOptimizedAst = compiled.optimizedAst === compiled.ast
86
+ ? translatedAst
87
+ : translateNode(compiled.optimizedAst, rowDelta, colDelta);
88
+ const translatedParsedDeps = compiled.parsedDeps?.map((dependency) => translateParsedDependencyReference(dependency, rowDelta, colDelta));
89
+ const translatedParsedSymbolicRefs = compiled.parsedSymbolicRefs?.map((reference) => translateParsedCellReference(reference, rowDelta, colDelta));
90
+ const translatedParsedSymbolicRanges = compiled.parsedSymbolicRanges?.map((range) => translateParsedRangeReference(range, rowDelta, colDelta));
91
+ const source = sourceOverride ?? serializeFormula(translatedAst);
92
+ return {
93
+ source,
94
+ compiled: {
95
+ ...compiled,
96
+ source,
97
+ ast: translatedAst,
98
+ optimizedAst: translatedOptimizedAst,
99
+ astMatchesSource: true,
100
+ deps: translatedParsedDeps?.map((dependency) => formatParsedDependencyReference(dependency)) ??
101
+ compiled.deps.map((dependency) => translateQualifiedDependencyReference(dependency, rowDelta, colDelta)),
102
+ symbolicRefs: translatedParsedSymbolicRefs?.map((reference) => formatParsedCellReference(reference)) ??
103
+ compiled.symbolicRefs.map((ref) => translateQualifiedCellReference(ref, rowDelta, colDelta)),
104
+ symbolicRanges: translatedParsedSymbolicRanges?.map((range) => formatParsedRangeReference(range)) ??
105
+ compiled.symbolicRanges.map((range) => translateQualifiedRangeReference(range, rowDelta, colDelta)),
106
+ jsPlan: compiled.jsPlan.map((instruction) => translateJsPlanInstruction(instruction, rowDelta, colDelta)),
107
+ ...(translatedParsedDeps ? { parsedDeps: translatedParsedDeps } : {}),
108
+ ...(translatedParsedSymbolicRefs ? { parsedSymbolicRefs: translatedParsedSymbolicRefs } : {}),
109
+ ...(translatedParsedSymbolicRanges
110
+ ? { parsedSymbolicRanges: translatedParsedSymbolicRanges }
111
+ : {}),
112
+ },
113
+ };
114
+ }
37
115
  export function rewriteFormulaForStructuralTransform(source, ownerSheetName, targetSheetName, transform) {
38
116
  const ast = parseFormula(source);
39
117
  return serializeFormula(rewriteNodeForStructuralTransform(ast, ownerSheetName, targetSheetName, transform));
40
118
  }
119
+ export function rewriteCompiledFormulaForStructuralTransform(compiled, ownerSheetName, targetSheetName, transform) {
120
+ const currentAst = compiled.astMatchesSource === false ? parseFormula(compiled.source) : compiled.ast;
121
+ const currentOptimizedAst = compiled.astMatchesSource === false
122
+ ? currentAst
123
+ : compiled.optimizedAst === compiled.ast
124
+ ? currentAst
125
+ : compiled.optimizedAst;
126
+ const rewrittenAst = rewriteNodeForStructuralTransform(currentAst, ownerSheetName, targetSheetName, transform);
127
+ const rewrittenOptimizedAst = currentOptimizedAst === currentAst
128
+ ? rewrittenAst
129
+ : rewriteNodeForStructuralTransform(currentOptimizedAst, ownerSheetName, targetSheetName, transform);
130
+ const source = serializeFormula(rewrittenAst);
131
+ if (!nodeStructuralShapeEqual(currentOptimizedAst, rewrittenOptimizedAst)) {
132
+ return {
133
+ source,
134
+ compiled: compileFormulaAst(source, rewrittenOptimizedAst, {
135
+ originalAst: rewrittenAst,
136
+ symbolicNames: compiled.symbolicNames,
137
+ symbolicTables: compiled.symbolicTables,
138
+ symbolicSpills: compiled.symbolicSpills,
139
+ }),
140
+ reusedProgram: false,
141
+ };
142
+ }
143
+ return {
144
+ source,
145
+ compiled: {
146
+ ...compiled,
147
+ source,
148
+ ast: rewrittenAst,
149
+ optimizedAst: rewrittenOptimizedAst,
150
+ astMatchesSource: true,
151
+ deps: compiled.deps.map((dependency) => rewriteQualifiedDependencyReference(dependency, ownerSheetName, targetSheetName, transform)),
152
+ symbolicRefs: compiled.symbolicRefs.map((ref) => rewriteQualifiedCellReference(ref, ownerSheetName, targetSheetName, transform)),
153
+ symbolicRanges: compiled.symbolicRanges.map((range) => rewriteQualifiedRangeReference(range, ownerSheetName, targetSheetName, transform)),
154
+ jsPlan: compiled.jsPlan.map((instruction) => rewriteJsPlanInstruction(instruction, ownerSheetName, targetSheetName, transform)),
155
+ ...(compiled.parsedDeps
156
+ ? {
157
+ parsedDeps: compiled.parsedDeps.map((dependency) => rewriteParsedDependencyReference(dependency, ownerSheetName, targetSheetName, transform)),
158
+ }
159
+ : {}),
160
+ ...(compiled.parsedSymbolicRefs
161
+ ? {
162
+ parsedSymbolicRefs: compiled.parsedSymbolicRefs.map((ref) => rewriteParsedCellReference(ref, ownerSheetName, targetSheetName, transform)),
163
+ }
164
+ : {}),
165
+ ...(compiled.parsedSymbolicRanges
166
+ ? {
167
+ parsedSymbolicRanges: compiled.parsedSymbolicRanges.map((range) => rewriteParsedRangeReference(range, ownerSheetName, targetSheetName, transform)),
168
+ }
169
+ : {}),
170
+ },
171
+ reusedProgram: true,
172
+ };
173
+ }
41
174
  export function renameFormulaSheetReferences(source, oldSheetName, newSheetName) {
42
175
  const ast = parseFormula(source);
43
176
  return serializeFormula(renameNodeSheetReferences(ast, oldSheetName, newSheetName));
@@ -141,6 +274,40 @@ function translateNode(node, rowDelta, colDelta) {
141
274
  };
142
275
  }
143
276
  }
277
+ function buildRelativeFormulaTemplateKeyInternal(node, ownerRow, ownerCol) {
278
+ switch (node.kind) {
279
+ case "NumberLiteral":
280
+ return `n:${node.value}`;
281
+ case "BooleanLiteral":
282
+ return node.value ? "b:1" : "b:0";
283
+ case "StringLiteral":
284
+ return `s:${JSON.stringify(node.value)}`;
285
+ case "ErrorLiteral":
286
+ return `e:${node.code}`;
287
+ case "NameRef":
288
+ return `name:${node.name}`;
289
+ case "StructuredRef":
290
+ return `table:${node.tableName}[${node.columnName}]`;
291
+ case "CellRef":
292
+ return `cell:${templateSheetKey(node.sheetName)}:${buildRelativeCellReferenceKey(node.ref, ownerRow, ownerCol)}`;
293
+ case "SpillRef":
294
+ return `spill:${templateSheetKey(node.sheetName)}:${buildRelativeCellReferenceKey(node.ref, ownerRow, ownerCol)}`;
295
+ case "ColumnRef":
296
+ return `col:${templateSheetKey(node.sheetName)}:${buildRelativeAxisReferenceKey(node.ref, ownerCol, "column")}`;
297
+ case "RowRef":
298
+ return `row:${templateSheetKey(node.sheetName)}:${buildRelativeAxisReferenceKey(node.ref, ownerRow, "row")}`;
299
+ case "RangeRef":
300
+ return `range:${node.refKind}:${templateSheetKey(node.sheetName)}:${buildRelativeRangeReferenceKey(node, ownerRow, ownerCol)}`;
301
+ case "UnaryExpr":
302
+ return `unary:${node.operator}:${buildRelativeFormulaTemplateKeyInternal(node.argument, ownerRow, ownerCol)}`;
303
+ case "BinaryExpr":
304
+ return `binary:${node.operator}:${buildRelativeFormulaTemplateKeyInternal(node.left, ownerRow, ownerCol)}:${buildRelativeFormulaTemplateKeyInternal(node.right, ownerRow, ownerCol)}`;
305
+ case "CallExpr":
306
+ return `call:${node.callee}:${node.args.map((arg) => buildRelativeFormulaTemplateKeyInternal(arg, ownerRow, ownerCol)).join("|")}`;
307
+ case "InvokeExpr":
308
+ return `invoke:${buildRelativeFormulaTemplateKeyInternal(node.callee, ownerRow, ownerCol)}:${node.args.map((arg) => buildRelativeFormulaTemplateKeyInternal(arg, ownerRow, ownerCol)).join("|")}`;
309
+ }
310
+ }
144
311
  function rewriteNodeForStructuralTransform(node, ownerSheetName, targetSheetName, transform) {
145
312
  switch (node.kind) {
146
313
  case "NumberLiteral":
@@ -192,6 +359,82 @@ function rewriteNodeForStructuralTransform(node, ownerSheetName, targetSheetName
192
359
  };
193
360
  }
194
361
  }
362
+ function templateSheetKey(sheetName) {
363
+ return sheetName === undefined ? "." : JSON.stringify(sheetName);
364
+ }
365
+ function buildRelativeCellReferenceKey(ref, ownerRow, ownerCol) {
366
+ const parsed = parseCellReferenceParts(ref);
367
+ if (!parsed) {
368
+ return `invalid:${ref}`;
369
+ }
370
+ const colKey = parsed.colAbsolute ? `ac${parsed.col}` : `rc${parsed.col - ownerCol}`;
371
+ const rowKey = parsed.rowAbsolute ? `ar${parsed.row}` : `rr${parsed.row - ownerRow}`;
372
+ return `${colKey}:${rowKey}`;
373
+ }
374
+ function buildRelativeAxisReferenceKey(ref, ownerIndex, kind) {
375
+ const parsed = parseAxisReferenceParts(ref, kind);
376
+ if (!parsed) {
377
+ return `invalid:${ref}`;
378
+ }
379
+ return parsed.absolute ? `a${parsed.index}` : `r${parsed.index - ownerIndex}`;
380
+ }
381
+ function buildRelativeRangeReferenceKey(node, ownerRow, ownerCol) {
382
+ switch (node.refKind) {
383
+ case "cells":
384
+ return `${buildRelativeCellReferenceKey(node.start, ownerRow, ownerCol)}:${buildRelativeCellReferenceKey(node.end, ownerRow, ownerCol)}`;
385
+ case "rows":
386
+ return `${buildRelativeAxisReferenceKey(node.start, ownerRow, "row")}:${buildRelativeAxisReferenceKey(node.end, ownerRow, "row")}`;
387
+ case "cols":
388
+ return `${buildRelativeAxisReferenceKey(node.start, ownerCol, "column")}:${buildRelativeAxisReferenceKey(node.end, ownerCol, "column")}`;
389
+ }
390
+ }
391
+ function nodeStructuralShapeEqual(left, right) {
392
+ if (left.kind !== right.kind) {
393
+ return false;
394
+ }
395
+ switch (left.kind) {
396
+ case "NumberLiteral":
397
+ return right.kind === "NumberLiteral" && left.value === right.value;
398
+ case "BooleanLiteral":
399
+ return right.kind === "BooleanLiteral" && left.value === right.value;
400
+ case "StringLiteral":
401
+ return right.kind === "StringLiteral" && left.value === right.value;
402
+ case "ErrorLiteral":
403
+ return right.kind === "ErrorLiteral" && left.code === right.code;
404
+ case "NameRef":
405
+ return right.kind === "NameRef" && left.name === right.name;
406
+ case "StructuredRef":
407
+ return (right.kind === "StructuredRef" &&
408
+ left.tableName === right.tableName &&
409
+ left.columnName === right.columnName);
410
+ case "CellRef":
411
+ case "SpillRef":
412
+ case "ColumnRef":
413
+ case "RowRef":
414
+ return true;
415
+ case "RangeRef":
416
+ return right.kind === "RangeRef" && left.refKind === right.refKind;
417
+ case "UnaryExpr":
418
+ return (right.kind === "UnaryExpr" &&
419
+ left.operator === right.operator &&
420
+ nodeStructuralShapeEqual(left.argument, right.argument));
421
+ case "BinaryExpr":
422
+ return (right.kind === "BinaryExpr" &&
423
+ left.operator === right.operator &&
424
+ nodeStructuralShapeEqual(left.left, right.left) &&
425
+ nodeStructuralShapeEqual(left.right, right.right));
426
+ case "CallExpr":
427
+ return (right.kind === "CallExpr" &&
428
+ left.callee === right.callee &&
429
+ left.args.length === right.args.length &&
430
+ left.args.every((arg, index) => nodeStructuralShapeEqual(arg, right.args[index])));
431
+ case "InvokeExpr":
432
+ return (right.kind === "InvokeExpr" &&
433
+ left.args.length === right.args.length &&
434
+ nodeStructuralShapeEqual(left.callee, right.callee) &&
435
+ left.args.every((arg, index) => nodeStructuralShapeEqual(arg, right.args[index])));
436
+ }
437
+ }
195
438
  function renameNodeSheetReferences(node, oldSheetName, newSheetName) {
196
439
  switch (node.kind) {
197
440
  case "NumberLiteral":
@@ -310,6 +553,698 @@ function rewriteRangeNode(node, ownerSheetName, targetSheetName, transform) {
310
553
  end: formatAxisReference(end.absolute, nextInterval.end, node.refKind === "rows" ? "row" : "column"),
311
554
  };
312
555
  }
556
+ function rewriteParsedCellReference(reference, ownerSheetName, targetSheetName, transform) {
557
+ const sheetName = reference.sheetName ?? ownerSheetName;
558
+ if (sheetName !== targetSheetName) {
559
+ return reference;
560
+ }
561
+ const nextAddress = rewriteAddressForStructuralTransform(reference.address, transform);
562
+ if (!nextAddress) {
563
+ return reference;
564
+ }
565
+ const parsed = parseCellAddress(nextAddress, sheetName);
566
+ return {
567
+ ...reference,
568
+ address: parsed.text,
569
+ ...(reference.sheetName !== undefined ? { sheetName: parsed.sheetName } : {}),
570
+ ...(reference.row !== undefined ? { row: parsed.row } : {}),
571
+ ...(reference.col !== undefined ? { col: parsed.col } : {}),
572
+ };
573
+ }
574
+ function rewriteParsedRangeReference(reference, ownerSheetName, targetSheetName, transform) {
575
+ const explicitSheetName = reference.sheetName;
576
+ if (!targetsSheet(explicitSheetName, ownerSheetName, targetSheetName)) {
577
+ return reference;
578
+ }
579
+ const nextRange = rewriteRangeAddressForStructuralTransform(parseRangeAddress(formatQualifiedRangeReference(explicitSheetName, reference.startAddress, reference.endAddress)), transform);
580
+ if (!nextRange) {
581
+ return reference;
582
+ }
583
+ const bounds = nextRange.kind === "cells"
584
+ ? {
585
+ startRow: nextRange.start.row,
586
+ endRow: nextRange.end.row,
587
+ startCol: nextRange.start.col,
588
+ endCol: nextRange.end.col,
589
+ }
590
+ : nextRange.kind === "rows"
591
+ ? {
592
+ startRow: nextRange.start.row,
593
+ endRow: nextRange.end.row,
594
+ startCol: 0,
595
+ endCol: 0,
596
+ }
597
+ : {
598
+ startRow: 0,
599
+ endRow: 0,
600
+ startCol: nextRange.start.col,
601
+ endCol: nextRange.end.col,
602
+ };
603
+ return {
604
+ ...reference,
605
+ address: formatQualifiedRangeReference(explicitSheetName, nextRange.start.text, nextRange.end.text),
606
+ refKind: nextRange.kind,
607
+ startAddress: nextRange.start.text,
608
+ endAddress: nextRange.end.text,
609
+ ...bounds,
610
+ };
611
+ }
612
+ function rewriteParsedDependencyReference(reference, ownerSheetName, targetSheetName, transform) {
613
+ return reference.kind === "cell"
614
+ ? rewriteParsedCellReference(reference, ownerSheetName, targetSheetName, transform)
615
+ : rewriteParsedRangeReference(reference, ownerSheetName, targetSheetName, transform);
616
+ }
617
+ function translateParsedCellReference(reference, rowDelta, colDelta) {
618
+ const parts = reference.rowAbsolute !== undefined &&
619
+ reference.colAbsolute !== undefined &&
620
+ reference.row !== undefined &&
621
+ reference.col !== undefined
622
+ ? {
623
+ row: reference.row,
624
+ col: reference.col,
625
+ rowAbsolute: reference.rowAbsolute,
626
+ colAbsolute: reference.colAbsolute,
627
+ }
628
+ : parseCellReferenceParts(reference.address);
629
+ if (!parts) {
630
+ return reference;
631
+ }
632
+ const nextRow = parts.rowAbsolute ? parts.row : parts.row + rowDelta;
633
+ const nextCol = parts.colAbsolute ? parts.col : parts.col + colDelta;
634
+ const nextLocalAddress = formatCellReference(parts, nextRow, nextCol);
635
+ const nextAddress = reference.explicitSheet || reference.sheetName !== undefined
636
+ ? formatQualifiedCellReference(reference.sheetName, nextLocalAddress)
637
+ : nextLocalAddress;
638
+ return {
639
+ ...reference,
640
+ address: nextAddress,
641
+ ...(reference.sheetName !== undefined ? { sheetName: reference.sheetName } : {}),
642
+ ...(reference.explicitSheet !== undefined ? { explicitSheet: reference.explicitSheet } : {}),
643
+ ...(reference.row !== undefined ? { row: nextRow } : {}),
644
+ ...(reference.col !== undefined ? { col: nextCol } : {}),
645
+ ...(reference.rowAbsolute !== undefined ? { rowAbsolute: parts.rowAbsolute } : {}),
646
+ ...(reference.colAbsolute !== undefined ? { colAbsolute: parts.colAbsolute } : {}),
647
+ };
648
+ }
649
+ function translateParsedRangeReference(reference, rowDelta, colDelta) {
650
+ const nextRange = translateParsedRangeReferenceInfo(reference, rowDelta, colDelta);
651
+ const bounds = nextRange.refKind === "cells"
652
+ ? {
653
+ startRow: nextRange.startRow,
654
+ endRow: nextRange.endRow,
655
+ startCol: nextRange.startCol,
656
+ endCol: nextRange.endCol,
657
+ }
658
+ : nextRange.refKind === "rows"
659
+ ? {
660
+ startRow: nextRange.startRow,
661
+ endRow: nextRange.endRow,
662
+ startCol: 0,
663
+ endCol: 0,
664
+ }
665
+ : {
666
+ startRow: 0,
667
+ endRow: 0,
668
+ startCol: nextRange.startCol,
669
+ endCol: nextRange.endCol,
670
+ };
671
+ return {
672
+ ...reference,
673
+ address: formatParsedRangeReference(nextRange),
674
+ refKind: nextRange.refKind,
675
+ startAddress: nextRange.startAddress,
676
+ endAddress: nextRange.endAddress,
677
+ ...bounds,
678
+ ...(reference.explicitSheet !== undefined ? { explicitSheet: reference.explicitSheet } : {}),
679
+ };
680
+ }
681
+ function translateParsedDependencyReference(reference, rowDelta, colDelta) {
682
+ return reference.kind === "cell"
683
+ ? translateParsedCellReference(reference, rowDelta, colDelta)
684
+ : translateParsedRangeReference(reference, rowDelta, colDelta);
685
+ }
686
+ function translateQualifiedCellReference(raw, rowDelta, colDelta) {
687
+ const explicitlyQualified = raw.includes("!");
688
+ const parsed = parseCellAddress(raw);
689
+ const nextAddress = translateCellReference(parsed.text, rowDelta, colDelta);
690
+ return explicitlyQualified
691
+ ? formatQualifiedCellReference(parsed.sheetName, nextAddress)
692
+ : nextAddress;
693
+ }
694
+ function formatParsedCellReference(reference) {
695
+ const localAddress = formatParsedLocalCellReference(reference);
696
+ return reference.explicitSheet || reference.sheetName !== undefined
697
+ ? formatQualifiedCellReference(reference.sheetName, localAddress)
698
+ : localAddress;
699
+ }
700
+ function formatParsedLocalCellReference(reference) {
701
+ const parts = reference.row !== undefined &&
702
+ reference.col !== undefined &&
703
+ reference.rowAbsolute !== undefined &&
704
+ reference.colAbsolute !== undefined
705
+ ? {
706
+ row: reference.row,
707
+ col: reference.col,
708
+ rowAbsolute: reference.rowAbsolute,
709
+ colAbsolute: reference.colAbsolute,
710
+ }
711
+ : parseCellReferenceParts(reference.address);
712
+ if (!parts) {
713
+ return stripSheetQualifier(reference.address);
714
+ }
715
+ return formatCellReference(parts, parts.row, parts.col);
716
+ }
717
+ function formatParsedRangeReference(reference) {
718
+ return formatQualifiedRangeReference(reference.explicitSheet ? reference.sheetName : undefined, reference.startAddress, reference.endAddress);
719
+ }
720
+ function stripSheetQualifier(reference) {
721
+ const bang = reference.lastIndexOf("!");
722
+ return bang === -1 ? reference : reference.slice(bang + 1);
723
+ }
724
+ function translatedCellInstructionKey(sheetName, address) {
725
+ return `${sheetName ?? ""}\t${address}`;
726
+ }
727
+ function translatedRangeInstructionKey(sheetName, refKind, start, end) {
728
+ return `${sheetName ?? ""}\t${refKind}\t${start}\t${end}`;
729
+ }
730
+ function buildTranslatedCellReferenceMap(original, translated) {
731
+ const output = new Map();
732
+ if (!original || !translated || original.length !== translated.length) {
733
+ return output;
734
+ }
735
+ for (let index = 0; index < original.length; index += 1) {
736
+ const source = original[index];
737
+ const target = translated[index];
738
+ if (!source || !target) {
739
+ continue;
740
+ }
741
+ output.set(translatedCellInstructionKey(source.sheetName, formatParsedLocalCellReference(source)), target);
742
+ }
743
+ return output;
744
+ }
745
+ function buildTranslatedRangeReferenceMap(original, translated) {
746
+ const output = new Map();
747
+ if (!original || !translated || original.length !== translated.length) {
748
+ return output;
749
+ }
750
+ for (let index = 0; index < original.length; index += 1) {
751
+ const source = original[index];
752
+ const target = translated[index];
753
+ if (!source || !target) {
754
+ continue;
755
+ }
756
+ output.set(translatedRangeInstructionKey(source.sheetName, source.refKind, source.startAddress, source.endAddress), target);
757
+ }
758
+ return output;
759
+ }
760
+ function formatParsedDependencyReference(reference) {
761
+ return reference.kind === "cell"
762
+ ? formatParsedCellReference(reference)
763
+ : formatParsedRangeReference(reference);
764
+ }
765
+ function translateQualifiedDependencyReference(raw, rowDelta, colDelta) {
766
+ if (!raw.includes(":")) {
767
+ return translateQualifiedCellReference(raw, rowDelta, colDelta);
768
+ }
769
+ return translateQualifiedRangeReference(raw, rowDelta, colDelta);
770
+ }
771
+ function translateQualifiedRangeReference(raw, rowDelta, colDelta) {
772
+ const explicitlyQualified = raw.includes("!");
773
+ const parsed = parseRangeAddress(raw);
774
+ const nextRange = translateRangeAddress(parsed, rowDelta, colDelta);
775
+ if (explicitlyQualified) {
776
+ return formatRangeAddress(nextRange);
777
+ }
778
+ return `${nextRange.start.text}:${nextRange.end.text}`;
779
+ }
780
+ function translateRangeAddress(range, rowDelta, colDelta) {
781
+ switch (range.kind) {
782
+ case "cells": {
783
+ const startAddress = translateCellReference(range.start.text, rowDelta, colDelta);
784
+ const endAddress = translateCellReference(range.end.text, rowDelta, colDelta);
785
+ return parseRangeAddress(formatQualifiedRangeReference(range.sheetName, startAddress, endAddress));
786
+ }
787
+ case "rows": {
788
+ const start = translateRowReference(range.start.text, rowDelta);
789
+ const end = translateRowReference(range.end.text, rowDelta);
790
+ return parseRangeAddress(formatQualifiedRangeReference(range.sheetName, start, end));
791
+ }
792
+ case "cols": {
793
+ const start = translateColumnReference(range.start.text, colDelta);
794
+ const end = translateColumnReference(range.end.text, colDelta);
795
+ return parseRangeAddress(formatQualifiedRangeReference(range.sheetName, start, end));
796
+ }
797
+ }
798
+ }
799
+ function translateParsedRangeReferenceInfo(reference, rowDelta, colDelta) {
800
+ if (reference.refKind === "cells") {
801
+ const startRow = (reference.startRowAbsolute ?? false) ? reference.startRow : reference.startRow + rowDelta;
802
+ const endRow = (reference.endRowAbsolute ?? false) ? reference.endRow : reference.endRow + rowDelta;
803
+ const startCol = (reference.startColAbsolute ?? false) ? reference.startCol : reference.startCol + colDelta;
804
+ const endCol = (reference.endColAbsolute ?? false) ? reference.endCol : reference.endCol + colDelta;
805
+ const startAddress = formatCellReference({
806
+ row: reference.startRow,
807
+ col: reference.startCol,
808
+ rowAbsolute: reference.startRowAbsolute ?? false,
809
+ colAbsolute: reference.startColAbsolute ?? false,
810
+ }, startRow, startCol);
811
+ const endAddress = formatCellReference({
812
+ row: reference.endRow,
813
+ col: reference.endCol,
814
+ rowAbsolute: reference.endRowAbsolute ?? false,
815
+ colAbsolute: reference.endColAbsolute ?? false,
816
+ }, endRow, endCol);
817
+ return {
818
+ ...reference,
819
+ startAddress,
820
+ endAddress,
821
+ startRow,
822
+ endRow,
823
+ startCol,
824
+ endCol,
825
+ };
826
+ }
827
+ if (reference.refKind === "rows") {
828
+ const startRow = (reference.startRowAbsolute ?? false) ? reference.startRow : reference.startRow + rowDelta;
829
+ const endRow = (reference.endRowAbsolute ?? false) ? reference.endRow : reference.endRow + rowDelta;
830
+ return {
831
+ ...reference,
832
+ startAddress: formatAxisReference(reference.startRowAbsolute ?? false, startRow, "row"),
833
+ endAddress: formatAxisReference(reference.endRowAbsolute ?? false, endRow, "row"),
834
+ startRow,
835
+ endRow,
836
+ startCol: 0,
837
+ endCol: 0,
838
+ };
839
+ }
840
+ const startCol = (reference.startColAbsolute ?? false) ? reference.startCol : reference.startCol + colDelta;
841
+ const endCol = (reference.endColAbsolute ?? false) ? reference.endCol : reference.endCol + colDelta;
842
+ return {
843
+ ...reference,
844
+ startAddress: formatAxisReference(reference.startColAbsolute ?? false, startCol, "column"),
845
+ endAddress: formatAxisReference(reference.endColAbsolute ?? false, endCol, "column"),
846
+ startRow: 0,
847
+ endRow: 0,
848
+ startCol,
849
+ endCol,
850
+ };
851
+ }
852
+ function rewriteQualifiedCellReference(raw, ownerSheetName, targetSheetName, transform) {
853
+ const explicitlyQualified = raw.includes("!");
854
+ const parsed = parseCellAddress(raw, ownerSheetName);
855
+ if (parsed.sheetName !== targetSheetName) {
856
+ return raw;
857
+ }
858
+ const nextAddress = rewriteAddressForStructuralTransform(parsed.text, transform);
859
+ if (!nextAddress) {
860
+ return raw;
861
+ }
862
+ return explicitlyQualified
863
+ ? formatQualifiedCellReference(parsed.sheetName, nextAddress)
864
+ : nextAddress;
865
+ }
866
+ function rewriteQualifiedDependencyReference(raw, ownerSheetName, targetSheetName, transform) {
867
+ if (!raw.includes(":")) {
868
+ return rewriteQualifiedCellReference(raw, ownerSheetName, targetSheetName, transform);
869
+ }
870
+ return rewriteQualifiedRangeReference(raw, ownerSheetName, targetSheetName, transform);
871
+ }
872
+ function rewriteQualifiedRangeReference(raw, ownerSheetName, targetSheetName, transform) {
873
+ const explicitlyQualified = raw.includes("!");
874
+ const parsed = parseRangeAddress(raw, ownerSheetName);
875
+ const sheetName = parsed.sheetName ?? ownerSheetName;
876
+ if (sheetName !== targetSheetName) {
877
+ return raw;
878
+ }
879
+ const nextRange = rewriteRangeAddressForStructuralTransform(parsed, transform);
880
+ if (!nextRange) {
881
+ return raw;
882
+ }
883
+ if (explicitlyQualified) {
884
+ return formatRangeAddress(nextRange);
885
+ }
886
+ return `${nextRange.start.text}:${nextRange.end.text}`;
887
+ }
888
+ function rewriteRangeAddressForStructuralTransform(range, transform) {
889
+ switch (range.kind) {
890
+ case "cells": {
891
+ const nextRange = rewriteRangeForStructuralTransform(range.start.text, range.end.text, transform);
892
+ if (!nextRange) {
893
+ return undefined;
894
+ }
895
+ return parseRangeAddress(formatQualifiedRangeReference(range.sheetName, nextRange.startAddress, nextRange.endAddress));
896
+ }
897
+ case "rows":
898
+ if (transform.axis !== "row") {
899
+ return range;
900
+ }
901
+ return rewriteAxisRangeAddress(range, transform);
902
+ case "cols":
903
+ if (transform.axis !== "column") {
904
+ return range;
905
+ }
906
+ return rewriteAxisRangeAddress(range, transform);
907
+ }
908
+ }
909
+ function rewriteAxisRangeAddress(range, transform) {
910
+ const startIndex = range.kind === "rows" ? range.start.row : range.start.col;
911
+ const endIndex = range.kind === "rows" ? range.end.row : range.end.col;
912
+ const nextInterval = mapInterval(startIndex, endIndex, transform);
913
+ if (!nextInterval) {
914
+ return undefined;
915
+ }
916
+ const prefix = range.sheetName ? `${quoteSheetNameIfNeeded(range.sheetName)}!` : "";
917
+ const startText = range.kind === "rows"
918
+ ? formatAxisReference(false, nextInterval.start, "row")
919
+ : formatAxisReference(false, nextInterval.start, "column");
920
+ const endText = range.kind === "rows"
921
+ ? formatAxisReference(false, nextInterval.end, "row")
922
+ : formatAxisReference(false, nextInterval.end, "column");
923
+ return parseRangeAddress(`${prefix}${startText}:${endText}`);
924
+ }
925
+ function rewriteJsPlanInstruction(instruction, ownerSheetName, targetSheetName, transform) {
926
+ switch (instruction.opcode) {
927
+ case "push-cell":
928
+ return {
929
+ ...instruction,
930
+ address: rewriteReferenceOperandAddress(instruction.sheetName, instruction.address, ownerSheetName, targetSheetName, transform),
931
+ };
932
+ case "push-range": {
933
+ const nextRange = rewritePlanRangeInstruction(instruction.sheetName, instruction.start, instruction.end, instruction.refKind, ownerSheetName, targetSheetName, transform);
934
+ return nextRange ? { ...instruction, ...nextRange } : instruction;
935
+ }
936
+ case "lookup-exact-match":
937
+ case "lookup-approximate-match": {
938
+ const nextRange = rewritePlanRangeInstruction(instruction.sheetName, instruction.start, instruction.end, instruction.refKind, ownerSheetName, targetSheetName, transform);
939
+ if (!nextRange) {
940
+ return instruction;
941
+ }
942
+ const parsed = parseRangeAddress(formatQualifiedRangeReference(instruction.sheetName, nextRange.start, nextRange.end));
943
+ if (parsed.kind !== "cells") {
944
+ return instruction;
945
+ }
946
+ return {
947
+ ...instruction,
948
+ ...nextRange,
949
+ startRow: parsed.start.row,
950
+ endRow: parsed.end.row,
951
+ startCol: parsed.start.col,
952
+ endCol: parsed.end.col,
953
+ };
954
+ }
955
+ case "call":
956
+ return instruction.argRefs
957
+ ? {
958
+ ...instruction,
959
+ argRefs: instruction.argRefs.map((argRef) => argRef
960
+ ? rewriteReferenceOperand(argRef, ownerSheetName, targetSheetName, transform)
961
+ : argRef),
962
+ }
963
+ : instruction;
964
+ case "push-lambda":
965
+ return {
966
+ ...instruction,
967
+ body: instruction.body.map((step) => rewriteJsPlanInstruction(step, ownerSheetName, targetSheetName, transform)),
968
+ };
969
+ case "push-number":
970
+ case "push-boolean":
971
+ case "push-string":
972
+ case "push-error":
973
+ case "push-name":
974
+ case "unary":
975
+ case "binary":
976
+ case "invoke":
977
+ case "begin-scope":
978
+ case "bind-name":
979
+ case "end-scope":
980
+ case "jump-if-false":
981
+ case "jump":
982
+ case "return":
983
+ return instruction;
984
+ }
985
+ }
986
+ function translateJsPlanInstruction(instruction, rowDelta, colDelta) {
987
+ switch (instruction.opcode) {
988
+ case "push-cell":
989
+ return {
990
+ ...instruction,
991
+ address: translateCellReference(instruction.address, rowDelta, colDelta),
992
+ };
993
+ case "push-range": {
994
+ const nextRange = translatePlanRangeInstruction(instruction.sheetName, instruction.start, instruction.end, rowDelta, colDelta);
995
+ return { ...instruction, ...nextRange };
996
+ }
997
+ case "lookup-exact-match":
998
+ case "lookup-approximate-match": {
999
+ const nextRange = translatePlanRangeInstruction(instruction.sheetName, instruction.start, instruction.end, rowDelta, colDelta);
1000
+ const parsed = parseRangeAddress(formatQualifiedRangeReference(instruction.sheetName, nextRange.start, nextRange.end));
1001
+ if (parsed.kind !== "cells") {
1002
+ return instruction;
1003
+ }
1004
+ return {
1005
+ ...instruction,
1006
+ ...nextRange,
1007
+ startRow: parsed.start.row,
1008
+ endRow: parsed.end.row,
1009
+ startCol: parsed.start.col,
1010
+ endCol: parsed.end.col,
1011
+ };
1012
+ }
1013
+ case "call":
1014
+ return instruction.argRefs
1015
+ ? {
1016
+ ...instruction,
1017
+ argRefs: instruction.argRefs.map((argRef) => argRef ? translateReferenceOperand(argRef, rowDelta, colDelta) : argRef),
1018
+ }
1019
+ : instruction;
1020
+ case "push-lambda":
1021
+ return {
1022
+ ...instruction,
1023
+ body: instruction.body.map((step) => translateJsPlanInstruction(step, rowDelta, colDelta)),
1024
+ };
1025
+ case "push-number":
1026
+ case "push-boolean":
1027
+ case "push-string":
1028
+ case "push-error":
1029
+ case "push-name":
1030
+ case "unary":
1031
+ case "binary":
1032
+ case "invoke":
1033
+ case "begin-scope":
1034
+ case "bind-name":
1035
+ case "end-scope":
1036
+ case "jump-if-false":
1037
+ case "jump":
1038
+ case "return":
1039
+ return instruction;
1040
+ }
1041
+ }
1042
+ function translateJsPlanInstructionWithoutAst(instruction, translatedCellMap, translatedRangeMap, rowDelta, colDelta) {
1043
+ switch (instruction.opcode) {
1044
+ case "push-cell": {
1045
+ const translated = translatedCellMap.get(translatedCellInstructionKey(instruction.sheetName, instruction.address));
1046
+ return translated
1047
+ ? {
1048
+ ...instruction,
1049
+ address: formatParsedLocalCellReference(translated),
1050
+ }
1051
+ : translateJsPlanInstruction(instruction, rowDelta, colDelta);
1052
+ }
1053
+ case "push-range": {
1054
+ const translated = translatedRangeMap.get(translatedRangeInstructionKey(instruction.sheetName, instruction.refKind, instruction.start, instruction.end));
1055
+ return translated
1056
+ ? {
1057
+ ...instruction,
1058
+ start: translated.startAddress,
1059
+ end: translated.endAddress,
1060
+ }
1061
+ : translateJsPlanInstruction(instruction, rowDelta, colDelta);
1062
+ }
1063
+ case "lookup-exact-match":
1064
+ case "lookup-approximate-match": {
1065
+ const translated = translatedRangeMap.get(translatedRangeInstructionKey(instruction.sheetName, instruction.refKind, instruction.start, instruction.end));
1066
+ if (!translated || translated.refKind !== "cells") {
1067
+ return translateJsPlanInstruction(instruction, rowDelta, colDelta);
1068
+ }
1069
+ return {
1070
+ ...instruction,
1071
+ start: translated.startAddress,
1072
+ end: translated.endAddress,
1073
+ startRow: translated.startRow,
1074
+ endRow: translated.endRow,
1075
+ startCol: translated.startCol,
1076
+ endCol: translated.endCol,
1077
+ };
1078
+ }
1079
+ case "call":
1080
+ return instruction.argRefs
1081
+ ? {
1082
+ ...instruction,
1083
+ argRefs: instruction.argRefs.map((argRef) => argRef
1084
+ ? translateReferenceOperandWithoutAst(argRef, translatedCellMap, translatedRangeMap, rowDelta, colDelta)
1085
+ : argRef),
1086
+ }
1087
+ : instruction;
1088
+ case "push-lambda":
1089
+ return {
1090
+ ...instruction,
1091
+ body: instruction.body.map((step) => translateJsPlanInstructionWithoutAst(step, translatedCellMap, translatedRangeMap, rowDelta, colDelta)),
1092
+ };
1093
+ case "push-number":
1094
+ case "push-boolean":
1095
+ case "push-string":
1096
+ case "push-error":
1097
+ case "push-name":
1098
+ case "unary":
1099
+ case "binary":
1100
+ case "invoke":
1101
+ case "begin-scope":
1102
+ case "bind-name":
1103
+ case "end-scope":
1104
+ case "jump-if-false":
1105
+ case "jump":
1106
+ case "return":
1107
+ return instruction;
1108
+ }
1109
+ }
1110
+ function rewriteReferenceOperand(operand, ownerSheetName, targetSheetName, transform) {
1111
+ switch (operand.kind) {
1112
+ case "cell":
1113
+ return operand.address
1114
+ ? {
1115
+ ...operand,
1116
+ address: rewriteReferenceOperandAddress(operand.sheetName, operand.address, ownerSheetName, targetSheetName, transform),
1117
+ }
1118
+ : operand;
1119
+ case "range": {
1120
+ if (!operand.start || !operand.end || !operand.refKind) {
1121
+ return operand;
1122
+ }
1123
+ const nextRange = rewritePlanRangeInstruction(operand.sheetName, operand.start, operand.end, operand.refKind, ownerSheetName, targetSheetName, transform);
1124
+ return nextRange ? { ...operand, ...nextRange } : operand;
1125
+ }
1126
+ case "row":
1127
+ case "col":
1128
+ return operand;
1129
+ }
1130
+ }
1131
+ function translateReferenceOperand(operand, rowDelta, colDelta) {
1132
+ switch (operand.kind) {
1133
+ case "cell":
1134
+ return operand.address
1135
+ ? {
1136
+ ...operand,
1137
+ address: translateCellReference(operand.address, rowDelta, colDelta),
1138
+ }
1139
+ : operand;
1140
+ case "range":
1141
+ if (!operand.start || !operand.end || !operand.refKind) {
1142
+ return operand;
1143
+ }
1144
+ return {
1145
+ ...operand,
1146
+ ...translatePlanRangeInstruction(operand.sheetName, operand.start, operand.end, rowDelta, colDelta),
1147
+ };
1148
+ case "row":
1149
+ return operand.address
1150
+ ? {
1151
+ ...operand,
1152
+ address: translateRowReference(operand.address, rowDelta),
1153
+ }
1154
+ : operand;
1155
+ case "col":
1156
+ return operand.address
1157
+ ? {
1158
+ ...operand,
1159
+ address: translateColumnReference(operand.address, colDelta),
1160
+ }
1161
+ : operand;
1162
+ }
1163
+ }
1164
+ function translateReferenceOperandWithoutAst(operand, translatedCellMap, translatedRangeMap, rowDelta, colDelta) {
1165
+ switch (operand.kind) {
1166
+ case "cell": {
1167
+ if (!operand.address) {
1168
+ return operand;
1169
+ }
1170
+ const translated = translatedCellMap.get(translatedCellInstructionKey(operand.sheetName, operand.address));
1171
+ return translated
1172
+ ? {
1173
+ ...operand,
1174
+ address: formatParsedLocalCellReference(translated),
1175
+ }
1176
+ : translateReferenceOperand(operand, rowDelta, colDelta);
1177
+ }
1178
+ case "range": {
1179
+ if (!operand.start || !operand.end || !operand.refKind) {
1180
+ return operand;
1181
+ }
1182
+ const translated = translatedRangeMap.get(translatedRangeInstructionKey(operand.sheetName, operand.refKind, operand.start, operand.end));
1183
+ return translated
1184
+ ? {
1185
+ ...operand,
1186
+ start: translated.startAddress,
1187
+ end: translated.endAddress,
1188
+ refKind: translated.refKind,
1189
+ }
1190
+ : translateReferenceOperand(operand, rowDelta, colDelta);
1191
+ }
1192
+ case "row":
1193
+ case "col":
1194
+ return translateReferenceOperand(operand, rowDelta, colDelta);
1195
+ }
1196
+ }
1197
+ function rewriteReferenceOperandAddress(explicitSheetName, address, ownerSheetName, targetSheetName, transform) {
1198
+ if ((explicitSheetName ?? ownerSheetName) !== targetSheetName) {
1199
+ return address;
1200
+ }
1201
+ return rewriteAddressForStructuralTransform(address, transform) ?? address;
1202
+ }
1203
+ function rewritePlanRangeInstruction(explicitSheetName, start, end, refKind, ownerSheetName, targetSheetName, transform) {
1204
+ if ((explicitSheetName ?? ownerSheetName) !== targetSheetName) {
1205
+ return undefined;
1206
+ }
1207
+ if (refKind === "cells") {
1208
+ const nextRange = rewriteRangeForStructuralTransform(start, end, transform);
1209
+ return nextRange
1210
+ ? {
1211
+ start: nextRange.startAddress,
1212
+ end: nextRange.endAddress,
1213
+ }
1214
+ : undefined;
1215
+ }
1216
+ if ((refKind === "rows" && transform.axis !== "row") ||
1217
+ (refKind === "cols" && transform.axis !== "column")) {
1218
+ return undefined;
1219
+ }
1220
+ const parsed = parseRangeAddress(formatQualifiedRangeReference(explicitSheetName, start, end));
1221
+ const nextRange = rewriteRangeAddressForStructuralTransform(parsed, transform);
1222
+ return nextRange
1223
+ ? {
1224
+ start: nextRange.start.text,
1225
+ end: nextRange.end.text,
1226
+ }
1227
+ : undefined;
1228
+ }
1229
+ function translatePlanRangeInstruction(explicitSheetName, start, end, rowDelta, colDelta) {
1230
+ const parsed = parseRangeAddress(formatQualifiedRangeReference(explicitSheetName, start, end));
1231
+ const nextRange = translateRangeAddress(parsed, rowDelta, colDelta);
1232
+ return {
1233
+ start: nextRange.start.text,
1234
+ end: nextRange.end.text,
1235
+ };
1236
+ }
1237
+ function formatQualifiedCellReference(sheetName, address) {
1238
+ if (!sheetName) {
1239
+ return address;
1240
+ }
1241
+ const parsed = parseCellAddress(address, sheetName);
1242
+ return `${quoteSheetNameIfNeeded(sheetName)}!${parsed.text}`;
1243
+ }
1244
+ function formatQualifiedRangeReference(sheetName, start, end) {
1245
+ const prefix = sheetName ? `${quoteSheetNameIfNeeded(sheetName)}!` : "";
1246
+ return `${prefix}${start}:${end}`;
1247
+ }
313
1248
  function translateCellReference(ref, rowDelta, colDelta) {
314
1249
  const parsed = parseCellReferenceParts(ref);
315
1250
  if (!parsed) {