@trebco/treb 29.3.4 → 29.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/treb.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /*! API v29.3. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
1
+ /*! API v29.4. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
2
2
 
3
3
  /**
4
4
  * add our tag to the map
@@ -743,13 +743,13 @@ export declare class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
743
743
  /**
744
744
  * set or clear cell valiation.
745
745
  *
746
- * @param address - target cell
746
+ * @param target - target cell/area
747
747
  * @param validation - a spreadsheet range, list of data, or undefined. pass
748
748
  * undefined to remove existing cell validation.
749
749
  * @param error - setting an invalid value in the target cell is an error (and
750
750
  * is blocked). defaults to false.
751
751
  */
752
- SetValidation(address: AddressReference, validation?: RangeReference | CellValue[], error?: boolean): void;
752
+ SetValidation(target: RangeReference, validation?: RangeReference | CellValue[], error?: boolean): void;
753
753
 
754
754
  /**
755
755
  * Delete a macro function.
@@ -1727,7 +1727,6 @@ export interface BaseCellData {
1727
1727
  table?: Table;
1728
1728
  area?: IArea;
1729
1729
  merge_area?: IArea;
1730
- validation?: DataValidation;
1731
1730
  calculated_type?: SerializedValueType;
1732
1731
  note?: string;
1733
1732
  hyperlink?: string;
@@ -1817,27 +1816,6 @@ export interface Table {
1817
1816
  */
1818
1817
  sort?: TableSortOptions;
1819
1818
  }
1820
- export type DataValidation = DataValidationList | DataValidationRange | DataValidationNumber | DataValidationDate | DataValidationBoolean;
1821
- export interface DataValidationBase {
1822
- error?: boolean;
1823
- }
1824
- export interface DataValidationRange extends DataValidationBase {
1825
- type: 'range';
1826
- area: IArea;
1827
- }
1828
- export interface DataValidationList extends DataValidationBase {
1829
- type: 'list';
1830
- list: CellValue[];
1831
- }
1832
- export interface DataValidationDate extends DataValidationBase {
1833
- type: 'date';
1834
- }
1835
- export interface DataValidationNumber extends DataValidationBase {
1836
- type: 'number';
1837
- }
1838
- export interface DataValidationBoolean extends DataValidationBase {
1839
- type: 'boolean';
1840
- }
1841
1819
 
1842
1820
  /**
1843
1821
  * string types for import/export
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "29.3.4",
3
+ "version": "29.4.1",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -584,6 +584,13 @@ export class Area implements IArea {
584
584
  && area.end_.column === this.end_.column;
585
585
  }
586
586
 
587
+ public Equals2(area: IArea): boolean {
588
+ return area.start.row === this.start_.row
589
+ && area.start.column === this.start_.column
590
+ && area.end.row === this.end_.row
591
+ && area.end.column === this.end_.column;
592
+ }
593
+
587
594
  public Clone(): Area {
588
595
  return new Area(this.start, this.end); // ensure copies
589
596
  }
@@ -21,7 +21,7 @@
21
21
 
22
22
  // import { Parser } from 'treb-parser';
23
23
 
24
- import type { Area, IArea } from './area';
24
+ import type { Area } from './area';
25
25
  import type { CellStyle } from './style';
26
26
  import type { TextPart } from './text_part';
27
27
  import type { Complex } from './value-type';
@@ -117,50 +117,6 @@ export type ClickFunction = (options: ClickFunctionOptions) => ClickFunctionResu
117
117
  * - raw per-cell style information. this is in a separate array (object).
118
118
  */
119
119
 
120
- /**
121
- * validation TODO: date, number, boolean, &c
122
- */
123
- export enum ValidationType {
124
- List = 'list',
125
- Date = 'date',
126
- Range = 'range',
127
- Number = 'number',
128
- Boolean = 'boolean',
129
- }
130
-
131
- export interface DataValidationBase {
132
- error?: boolean;
133
- }
134
-
135
- export interface DataValidationRange extends DataValidationBase {
136
- type: 'range'; // ValidationType.Range;
137
- area: IArea;
138
- }
139
-
140
- export interface DataValidationList extends DataValidationBase {
141
- type: 'list'; // ValidationType.List;
142
- list: CellValue[];
143
- }
144
-
145
- export interface DataValidationDate extends DataValidationBase {
146
- type: 'date'; // ValidationType.Date;
147
- }
148
-
149
- export interface DataValidationNumber extends DataValidationBase {
150
- type: 'number'; // ValidationType.Number;
151
- }
152
-
153
- export interface DataValidationBoolean extends DataValidationBase {
154
- type: 'boolean'; // ValidationType.Boolean;
155
- }
156
-
157
- export type DataValidation
158
- = DataValidationList
159
- | DataValidationRange
160
- | DataValidationNumber
161
- | DataValidationDate
162
- | DataValidationBoolean;
163
-
164
120
  export class Cell {
165
121
 
166
122
  // --- static methods -------------------------------------------------------
@@ -342,7 +298,7 @@ export class Cell {
342
298
  /** not editable */
343
299
  // public locked?: boolean;
344
300
 
345
- public validation?: DataValidation;
301
+ // public validation?: DataValidation;
346
302
 
347
303
  // --- class methods --------------------------------------------------------
348
304
 
@@ -26,7 +26,9 @@
26
26
 
27
27
  import type { IArea, ICellAddress} from './area';
28
28
  import { Area, IsCellAddress } from './area';
29
- import type { DataValidation } from './cell';
29
+
30
+ // import type { DataValidation } from './cell';
31
+
30
32
  import { Cell } from './cell';
31
33
  import type { Table } from './table';
32
34
  import { type SerializedValueType, ValueType, GetValueType, ValueTypeList } from './value-type';
@@ -79,7 +81,7 @@ export interface BaseCellData {
79
81
  table?: Table;
80
82
  area?: IArea;
81
83
  merge_area?: IArea;
82
- validation?: DataValidation;
84
+ // validation?: DataValidation;
83
85
  calculated_type?: SerializedValueType; // ValueType;
84
86
  note?: string;
85
87
  hyperlink?: string;
@@ -404,7 +406,7 @@ export class Cells {
404
406
 
405
407
 
406
408
 
407
- /**
409
+ /* *
408
410
  * this method is used for importing legacy data validation types. in those
409
411
  * those we used a numeric enum. we're just dropping that altogether (c.f.
410
412
  * ValueType, which we're keeping) so we need to translate for backcompat.
@@ -419,7 +421,8 @@ export class Cells {
419
421
  * Boolean = 'boolean',
420
422
  * }
421
423
  *
422
- */
424
+ * OK, removed
425
+ * /
423
426
  public ImportDataValidation(value: DataValidation): DataValidation|undefined {
424
427
 
425
428
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -435,6 +438,7 @@ export class Cells {
435
438
 
436
439
  return value;
437
440
  }
441
+ */
438
442
 
439
443
  /**
440
444
  * UPDATE: adding optional style refs, for export
@@ -572,6 +576,7 @@ export class Cells {
572
576
  }
573
577
  }
574
578
 
579
+ /*
575
580
  if (obj.validation) {
576
581
 
577
582
  // the old type used a numeric enum. we just dropped that in favor
@@ -581,6 +586,7 @@ export class Cells {
581
586
  cell.validation = this.ImportDataValidation(obj.validation);
582
587
 
583
588
  }
589
+ */
584
590
 
585
591
  }
586
592
 
@@ -678,7 +684,7 @@ export class Cells {
678
684
  (merge_head || cell.type || (cell.calculated_type && options.expand_arrays) ||
679
685
  (cell.calculated_type && options.calculated_value) ||
680
686
  (cell.note) ||
681
- (cell.validation) ||
687
+ // (cell.validation) ||
682
688
  (options.decorated_cells && cell.style &&
683
689
  ( cell.style.fill || cell.style.border_bottom ||
684
690
  cell.style.border_top || cell.style.border_left || cell.style.border_right)))){
@@ -715,9 +721,9 @@ export class Cells {
715
721
  if (cell.merge_area) {
716
722
  obj.merge_area = cell.merge_area.toJSON();
717
723
  }
718
- if (cell.validation) {
719
- obj.validation = cell.validation; // safe?
720
- }
724
+ // if (cell.validation) {
725
+ // obj.validation = cell.validation; // safe?
726
+ // }
721
727
 
722
728
  if (options.cell_style_refs &&
723
729
  options.cell_style_refs[column] &&
@@ -23,9 +23,8 @@ import type { CellStyle } from './style';
23
23
  import type { SerializedValueType } from './value-type';
24
24
  import type { IArea } from './area';
25
25
  import type { AnnotationLayout } from './layout';
26
- import type { DataValidation } from './cell';
27
26
  import type { Table } from './table';
28
- import type { AnnotationType, ConditionalFormat } from 'treb-data-model';
27
+ import type { DataValidation, AnnotationType, ConditionalFormat } from 'treb-data-model';
29
28
 
30
29
  export interface CellParseResult {
31
30
  row: number,
@@ -60,6 +59,7 @@ export interface ImportedSheetData {
60
59
  row_heights: number[];
61
60
  styles: CellStyle[];
62
61
  conditional_formats: ConditionalFormat[];
62
+ data_validations?: DataValidation[];
63
63
 
64
64
  // optional, for backcompat
65
65
  sheet_style?: number;
@@ -184,7 +184,7 @@ export class Calculator extends Graph {
184
184
 
185
185
  super();
186
186
 
187
- this.expression_calculator = new ExpressionCalculator(this.library, this.parser);
187
+ this.expression_calculator = new ExpressionCalculator(this.model, this.library, this.parser);
188
188
 
189
189
 
190
190
  // at the moment options are only used here; in the future
@@ -208,6 +208,9 @@ export class Calculator extends Graph {
208
208
 
209
209
  }
210
210
 
211
+ // FIXME: why is this called here, if model now owns it?
212
+ // TODO: move to model
213
+
211
214
  this.UpdateLocale(); // for parser
212
215
 
213
216
  // base functions
@@ -1391,6 +1394,11 @@ export class Calculator extends Graph {
1391
1394
  };
1392
1395
  });
1393
1396
 
1397
+ // FIXME: this doesn't need to be here, if it's owned by model.
1398
+ // we should have model responsible for retrieving these names
1399
+ // (along with named ranges/expressions). also, should macro
1400
+ // functions support scoping?
1401
+
1394
1402
  for (const macro of this.model.macro_functions.values()) {
1395
1403
  function_list.push({
1396
1404
  name: macro.name,
@@ -1448,8 +1456,7 @@ export class Calculator extends Graph {
1448
1456
  // that rely on it. which is a pretty far-fetched scenario, but we might
1449
1457
  // as well protect against it.
1450
1458
 
1451
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1452
- descriptor.fn = (...args: any[]) => {
1459
+ descriptor.fn = (...args: unknown[]) => {
1453
1460
  return original_function.apply({
1454
1461
  address: { ...this.expression_calculator.context.address},
1455
1462
  }, args);
@@ -1460,22 +1467,23 @@ export class Calculator extends Graph {
1460
1467
 
1461
1468
  }
1462
1469
 
1463
- /**
1470
+ /* *
1464
1471
  * wrap the attachdata function so we can update the expression calculator
1465
1472
  * at the same time (we should unwind this a little bit, it's an artifact
1466
1473
  * of graph being a separate class)
1467
- */
1474
+ * /
1468
1475
  public AttachModel(): void {
1469
1476
  // this.RebuildMap();
1470
- this.expression_calculator.SetModel(this.model);
1477
+ // this.expression_calculator.SetModel(this.model);
1471
1478
  }
1479
+ */
1472
1480
 
1473
1481
  /**
1474
1482
  * wrapper method for calculation
1475
1483
  */
1476
1484
  public Calculate(subset?: Area): void {
1477
1485
 
1478
- this.AttachModel();
1486
+ // this.AttachModel();
1479
1487
 
1480
1488
  // this gets checked later, now... it would be better if we could
1481
1489
  // check it here are skip the later check, but that field is optional
@@ -1539,7 +1547,7 @@ export class Calculator extends Graph {
1539
1547
  public Reset(): void {
1540
1548
 
1541
1549
  this.FlushTree();
1542
- this.AttachModel();
1550
+ // this.AttachModel();
1543
1551
 
1544
1552
  this.full_rebuild_required = true;
1545
1553
  }
@@ -1572,7 +1580,10 @@ export class Calculator extends Graph {
1572
1580
  return map;
1573
1581
  }
1574
1582
 
1583
+ /** overload */
1575
1584
  public Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: false): CellValue|CellValue[][];
1585
+
1586
+ /** overload */
1576
1587
  public Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: true): UnionValue;
1577
1588
 
1578
1589
  /** moved from embedded sheet */
@@ -1687,8 +1698,6 @@ export class Calculator extends Graph {
1687
1698
 
1688
1699
  this.full_rebuild_required = false; // unset
1689
1700
 
1690
- this.AttachModel();
1691
-
1692
1701
  this.RebuildGraph();
1693
1702
 
1694
1703
  // add leaf vertices for annotations
@@ -1955,7 +1964,7 @@ export class Calculator extends Graph {
1955
1964
  }
1956
1965
 
1957
1966
  /**
1958
- *
1967
+ * FIXME: just add a quote option to the model method and we can drop this function
1959
1968
  */
1960
1969
  public ResolveSheetName(id: number, quote = false): string | undefined {
1961
1970
  const sheet = this.model.sheets.Find(id);
@@ -2792,10 +2801,11 @@ export class Calculator extends Graph {
2792
2801
 
2793
2802
  }
2794
2803
 
2795
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2804
+ /*
2796
2805
  protected IsNativeOrTypedArray(val: unknown): boolean {
2797
2806
  return Array.isArray(val) || (val instanceof Float64Array) || (val instanceof Float32Array);
2798
2807
  }
2808
+ */
2799
2809
 
2800
2810
  /**
2801
2811
  * check if a cell is volatile. normally this falls out of the calculation,
@@ -639,6 +639,8 @@ export abstract class Graph implements GraphCallbacks {
639
639
 
640
640
  const map = this.vertices[u.start.sheet_id];
641
641
 
642
+ // console.info({u});
643
+
642
644
  // this might happen on create, we can let it go because the
643
645
  // references will be added when the relevant sheet is added
644
646
 
@@ -680,10 +682,17 @@ export abstract class Graph implements GraphCallbacks {
680
682
  else {
681
683
  for (let row = u.start.row; row <= u.end.row; row++) {
682
684
  for (let column = u.start.column; column <= u.end.column; column++) {
683
- const vertex = map[column][row];
684
- if (vertex) {
685
- array_vertex.DependsOn(vertex);
685
+ if (map[column]) {
686
+ const vertex = map[column][row];
687
+ if (vertex) {
688
+ array_vertex.DependsOn(vertex);
689
+ }
690
+ }
691
+ /*
692
+ else {
693
+ console.info("HERE", column, row);
686
694
  }
695
+ */
687
696
  }
688
697
  }
689
698
  }
@@ -27,7 +27,7 @@ import type { Cell, ICellAddress,
27
27
  UndefinedUnion,
28
28
  ComplexUnion,
29
29
  DimensionedQuantityUnion} from 'treb-base-types';
30
- import { ValueType, GetValueType } from 'treb-base-types';
30
+ import { ValueType, GetValueType, Area } from 'treb-base-types';
31
31
  import type { Parser, ExpressionUnit, UnitBinary, UnitIdentifier,
32
32
  UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference } from 'treb-parser';
33
33
  import type { DataModel, MacroFunction, Sheet } from 'treb-data-model';
@@ -36,8 +36,26 @@ import { ReturnType } from './descriptors';
36
36
 
37
37
  import * as Primitives from './primitives';
38
38
 
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- export type ExtendedExpressionUnit = ExpressionUnit & { user_data: any }; // export for MC overload
39
+ //////////
40
+
41
+ /**
42
+ * dynamically adding a user data field to the expression so we can
43
+ * cache a function call, avoiding type switching and function lookups.
44
+ *
45
+ * we use the generic type so we can cover the composite type as well
46
+ * before specifying
47
+ */
48
+ type AttachCachedFunction<T> = (T & { fn: (arg0: T) => UnionValue });
49
+
50
+ /**
51
+ * expression unit with cached function
52
+ */
53
+ type ExpressionWithCachedFunction<T extends ExpressionUnit> = T extends { type: T['type'] } ? AttachCachedFunction<T> : never;
54
+
55
+ /**
56
+ * @internal
57
+ */
58
+ export type ExtendedExpressionUnit = ExpressionWithCachedFunction<ExpressionUnit>;
41
59
 
42
60
  // FIXME: move
43
61
  export const UnionIsExpressionUnit = (test: UnionValue /*UnionOrArray*/): test is { type: ValueType.object, value: ExpressionUnit } => {
@@ -61,16 +79,18 @@ export const UnionIsMetadata = (test: UnionValue /*UnionOrArray*/): test is { ty
61
79
  // FIXME: move
62
80
  export interface ReferenceMetadata {
63
81
  type: 'metadata';
82
+
83
+ // what's the context in which I was using the unit address (parse expression?)
64
84
  address: UnitAddress; // ICellAddress;
85
+
65
86
  value: CellValue;
66
87
  format?: string;
67
88
  }
68
89
 
69
90
  export interface CalculationContext {
70
91
  address: ICellAddress;
71
- model?: DataModel;
92
+ // model?: DataModel;
72
93
  volatile: boolean;
73
- call_index: number;
74
94
  }
75
95
 
76
96
  export class ExpressionCalculator {
@@ -78,57 +98,30 @@ export class ExpressionCalculator {
78
98
  public context: CalculationContext = {
79
99
  address: { row: -1, column: -1 },
80
100
  volatile: false,
81
- call_index: 0,
82
101
  };
83
102
 
84
- /**
85
- * this refers to the number of function call within a single cell.
86
- * so if you have a function like
87
- *
88
- * =A(B())
89
- *
90
- * then when calculating A call index should be set to 1; and when
91
- * calculating B, call index is 2. and so on.
92
- */
93
- protected call_index = 0;
94
-
95
- // local reference
96
- // protected cells: Cells = new Cells();
97
- // protected cells_map: {[index: number]: Cells} = {};
98
- // protected sheet_name_map: {[index: string]: number} = {};
99
-
100
- // local reference
101
- // protected named_range_map: {[index: string]: Area} = {};
102
-
103
- // protected bound_name_stack: Array<Record<string, ExpressionUnit>> = [];
104
-
105
103
  //
106
- protected data_model!: DataModel;
104
+ // protected data_model!: DataModel; // can we set in ctor? I think this is a legacy hack
107
105
 
108
106
 
109
107
  // --- public API -----------------------------------------------------------
110
108
 
111
109
  constructor(
110
+ protected readonly data_model: DataModel,
112
111
  protected readonly library: FunctionLibrary,
113
112
  protected readonly parser: Parser) {}
114
113
 
114
+ /*
115
115
  public SetModel(model: DataModel): void {
116
116
 
117
- // this.cells_map = {};
118
- // this.sheet_name_map = {};
119
-
120
- /*
121
- for (const sheet of model.sheets.list) {
122
- // this.cells_map[sheet.id] = sheet.cells;
123
- // this.sheet_name_map[sheet.name.toLowerCase()] = sheet.id;
124
- }
125
- */
117
+ // is this kept around for some side-effects or something? does
118
+ // the model ever change?
126
119
 
127
120
  this.data_model = model;
128
- // this.named_range_map = model.named_ranges.Map();
129
121
  this.context.model = model;
130
122
 
131
123
  }
124
+ */
132
125
 
133
126
  /**
134
127
  * there's a case where we are calling this from within a function
@@ -141,10 +134,6 @@ export class ExpressionCalculator {
141
134
 
142
135
  this.context.address = addr;
143
136
  this.context.volatile = false;
144
- this.context.call_index = 0;
145
-
146
- // reset for this cell
147
- this.call_index = 0; // why not in model? A: timing (nested)
148
137
 
149
138
  }
150
139
 
@@ -218,8 +207,7 @@ export class ExpressionCalculator {
218
207
  }
219
208
 
220
209
  /** breaking this out to de-dupe */
221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
- protected GetMetadata(arg: ExpressionUnit, map_result: (cell_data: Cell, address: ICellAddress) => any): UnionValue /*UnionOrArray*/ {
210
+ protected GetMetadata<T>(arg: ExpressionUnit, transform: (cell_data: Cell, address: ICellAddress) => T): UnionValue {
223
211
 
224
212
  // FIXME: we used to restrict this to non-cell functions, now
225
213
  // we are using it for the cell function (we used to use address,
@@ -328,10 +316,24 @@ export class ExpressionCalculator {
328
316
 
329
317
  const metadata: ReferenceMetadata = {
330
318
  type: 'metadata',
331
- address: {...address},
319
+
320
+ // metadata is expecting a parse expression instead of an addresss.
321
+ // note we're not setting the label properly here, which could be
322
+ // an issue? not sure who's calling it in this case
323
+
324
+ // UPDATE: "Cell" is calling it, so it needs a label
325
+
326
+ address: {
327
+ ...address,
328
+ position: 0,
329
+ id: 0,
330
+ type: 'address',
331
+ label: new Area(address).spreadsheet_label,
332
+ },
333
+
332
334
  value,
333
335
  format: cell_data.style ? cell_data.style.number_format : undefined,
334
- ...map_result(cell_data, address),
336
+ ...transform(cell_data, address),
335
337
  };
336
338
 
337
339
  return { type: ValueType.object, value: metadata, key: 'metadata' };
@@ -378,7 +380,7 @@ export class ExpressionCalculator {
378
380
  address,
379
381
  value,
380
382
  format: cell_data.style ? cell_data.style.number_format : undefined,
381
- ...map_result(cell_data, address),
383
+ ...transform(cell_data, address),
382
384
  };
383
385
 
384
386
  column_result.push({
@@ -511,11 +513,6 @@ export class ExpressionCalculator {
511
513
 
512
514
  return (expr: UnitCall) => {
513
515
 
514
- // get an index we can use for this call (we may recurse when
515
- // calculating arguments), then increment for the next call.
516
-
517
- const call_index = this.call_index++;
518
-
519
516
  // yeah so this is clear. just checking volatile.
520
517
 
521
518
  // FIXME: should this be set later, at the same time as the
@@ -656,11 +653,6 @@ export class ExpressionCalculator {
656
653
  return argument_error;
657
654
  }
658
655
 
659
- // if we have any nested calls, they may have updated the index so
660
- // we use the captured value here.
661
-
662
- this.context.call_index = call_index;
663
-
664
656
  // I thought we were passing the model as this (...) ? actually
665
657
  // now we bind functions that need this, so maybe we should pass
666
658
  // null here.
@@ -988,7 +980,7 @@ export class ExpressionCalculator {
988
980
  return (expr: UnitGroup) => this.CalculateExpression(expr.elements[0] as ExtendedExpressionUnit);
989
981
  }
990
982
 
991
- protected CalculateExpression(expr: ExtendedExpressionUnit, return_reference = false): UnionValue /*UnionOrArray*/ {
983
+ protected CalculateExpression(expr: ExtendedExpressionUnit, return_reference = false): UnionValue {
992
984
 
993
985
  // user data is a generated function for the expression, at least
994
986
  // for the simple ones (atm). see BinaryExpression for more. the
@@ -996,8 +988,8 @@ export class ExpressionCalculator {
996
988
 
997
989
  // may be over-optimizing here.
998
990
 
999
- if (expr.user_data) {
1000
- return expr.user_data(expr);
991
+ if ((expr as AttachCachedFunction<ExpressionUnit>).fn) {
992
+ return (expr as AttachCachedFunction<ExpressionUnit>).fn(expr);
1001
993
  }
1002
994
 
1003
995
  switch (expr.type){
@@ -1005,52 +997,52 @@ export class ExpressionCalculator {
1005
997
  {
1006
998
  const macro = this.data_model.macro_functions.get(expr.name.toUpperCase());
1007
999
  if (macro) {
1008
- return (expr.user_data = this.CallMacro(expr, macro))(expr);
1000
+ return (expr.fn = this.CallMacro(expr, macro))(expr);
1009
1001
  }
1010
- return (expr.user_data = this.CallExpression(expr, return_reference))(expr);
1002
+ return (expr.fn = this.CallExpression(expr, return_reference))(expr);
1011
1003
  }
1012
1004
 
1013
1005
  case 'address':
1014
- return (expr.user_data = this.CellFunction2(expr))(); // check
1006
+ return (expr.fn = this.CellFunction2(expr))(); // check
1015
1007
 
1016
1008
  case 'range':
1017
- return (expr.user_data = (x: UnitRange) => this.CellFunction4(x.start, x.end))(expr); // check
1009
+ return (expr.fn = (x: UnitRange) => this.CellFunction4(x.start, x.end))(expr); // check
1018
1010
 
1019
1011
  case 'binary':
1020
- return (expr.user_data = this.BinaryExpression(expr))(expr); // check
1012
+ return (expr.fn = this.BinaryExpression(expr))(expr); // check
1021
1013
 
1022
1014
  case 'unary':
1023
- return (expr.user_data = this.UnaryExpression(expr))(expr); // check
1015
+ return (expr.fn = this.UnaryExpression(expr))(expr); // check
1024
1016
 
1025
1017
  case 'identifier':
1026
- return (expr.user_data = this.Identifier(expr))(); // check
1018
+ return (expr.fn = this.Identifier(expr))(); // check
1027
1019
 
1028
1020
  case 'missing':
1029
- return (expr.user_data = () => { return { value: undefined, type: ValueType.undefined } as UndefinedUnion })(); // check
1021
+ return (expr.fn = () => { return { value: undefined, type: ValueType.undefined } as UndefinedUnion })(); // check
1030
1022
 
1031
1023
  case 'dimensioned':
1032
- return (expr.user_data = this.ResolveDimensionedQuantity())(expr);
1024
+ return (expr.fn = this.ResolveDimensionedQuantity())(expr);
1033
1025
 
1034
1026
  case 'literal':
1035
1027
  {
1036
1028
  const literal = { value: expr.value, type: GetValueType(expr.value) } as UnionValue;
1037
- return (expr.user_data = () => literal)(); // check
1029
+ return (expr.fn = () => literal)(); // check
1038
1030
  }
1039
1031
  case 'group':
1040
- return (expr.user_data = this.GroupExpression(expr))(expr); // check
1032
+ return (expr.fn = this.GroupExpression(expr))(expr); // check
1041
1033
 
1042
1034
  case 'complex':
1043
1035
  {
1044
1036
  const literal = {value: {real: expr.real, imaginary: expr.imaginary}, type: ValueType.complex } as ComplexUnion;
1045
- return (expr.user_data = () => literal)(); // check
1037
+ return (expr.fn = () => literal)(); // check
1046
1038
  }
1047
1039
 
1048
1040
  case 'structured-reference':
1049
- return (expr.user_data = this.ResolveStructuredReference(expr))();
1041
+ return (expr.fn = this.ResolveStructuredReference(expr))();
1050
1042
 
1051
1043
  case 'array':
1052
1044
  {
1053
- return (expr.user_data = () => {
1045
+ return (expr.fn = () => {
1054
1046
  return {
1055
1047
  type: ValueType.array,
1056
1048
  value: expr.values.map((row) => (Array.isArray(row) ? row : [row]).map((value) => {