@trebco/treb 23.6.2 → 25.0.0-rc1

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 (217) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.js +164 -0
  3. package/README-shadow-DOM.md +88 -0
  4. package/README.md +37 -130
  5. package/api-config.json +29 -0
  6. package/api-generator/api-generator-types.ts +82 -0
  7. package/api-generator/api-generator.ts +1172 -0
  8. package/api-generator/package.json +3 -0
  9. package/build/treb-spreadsheet.mjs +14 -0
  10. package/{treb.d.ts → build/treb.d.ts} +293 -299
  11. package/esbuild-custom-element.mjs +336 -0
  12. package/esbuild.js +305 -0
  13. package/package.json +43 -14
  14. package/treb-base-types/package.json +5 -0
  15. package/treb-base-types/src/api_types.ts +36 -0
  16. package/treb-base-types/src/area.ts +583 -0
  17. package/treb-base-types/src/basic_types.ts +45 -0
  18. package/treb-base-types/src/cell.ts +612 -0
  19. package/treb-base-types/src/cells.ts +1066 -0
  20. package/treb-base-types/src/color.ts +124 -0
  21. package/treb-base-types/src/import.ts +71 -0
  22. package/treb-base-types/src/index-standalone.ts +29 -0
  23. package/treb-base-types/src/index.ts +42 -0
  24. package/treb-base-types/src/layout.ts +47 -0
  25. package/treb-base-types/src/localization.ts +187 -0
  26. package/treb-base-types/src/rectangle.ts +145 -0
  27. package/treb-base-types/src/render_text.ts +72 -0
  28. package/treb-base-types/src/style.ts +545 -0
  29. package/treb-base-types/src/table.ts +109 -0
  30. package/treb-base-types/src/text_part.ts +54 -0
  31. package/treb-base-types/src/theme.ts +608 -0
  32. package/treb-base-types/src/union.ts +152 -0
  33. package/treb-base-types/src/value-type.ts +164 -0
  34. package/treb-base-types/style/resizable.css +59 -0
  35. package/treb-calculator/modern.tsconfig.json +11 -0
  36. package/treb-calculator/package.json +5 -0
  37. package/treb-calculator/src/calculator.ts +2546 -0
  38. package/treb-calculator/src/complex-math.ts +558 -0
  39. package/treb-calculator/src/dag/array-vertex.ts +198 -0
  40. package/treb-calculator/src/dag/graph.ts +951 -0
  41. package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
  42. package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
  43. package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
  44. package/treb-calculator/src/dag/vertex.ts +352 -0
  45. package/treb-calculator/src/descriptors.ts +162 -0
  46. package/treb-calculator/src/expression-calculator.ts +1069 -0
  47. package/treb-calculator/src/function-error.ts +103 -0
  48. package/treb-calculator/src/function-library.ts +103 -0
  49. package/treb-calculator/src/functions/base-functions.ts +1214 -0
  50. package/treb-calculator/src/functions/checkbox.ts +164 -0
  51. package/treb-calculator/src/functions/complex-functions.ts +253 -0
  52. package/treb-calculator/src/functions/finance-functions.ts +399 -0
  53. package/treb-calculator/src/functions/information-functions.ts +102 -0
  54. package/treb-calculator/src/functions/matrix-functions.ts +182 -0
  55. package/treb-calculator/src/functions/sparkline.ts +335 -0
  56. package/treb-calculator/src/functions/statistics-functions.ts +350 -0
  57. package/treb-calculator/src/functions/text-functions.ts +298 -0
  58. package/treb-calculator/src/index.ts +27 -0
  59. package/treb-calculator/src/notifier-types.ts +59 -0
  60. package/treb-calculator/src/primitives.ts +428 -0
  61. package/treb-calculator/src/utilities.ts +305 -0
  62. package/treb-charts/package.json +5 -0
  63. package/treb-charts/src/chart-functions.ts +156 -0
  64. package/treb-charts/src/chart-types.ts +230 -0
  65. package/treb-charts/src/chart.ts +1288 -0
  66. package/treb-charts/src/index.ts +24 -0
  67. package/treb-charts/src/main.ts +37 -0
  68. package/treb-charts/src/rectangle.ts +52 -0
  69. package/treb-charts/src/renderer.ts +1841 -0
  70. package/treb-charts/src/util.ts +122 -0
  71. package/treb-charts/style/charts.scss +221 -0
  72. package/treb-charts/style/old-charts.scss +250 -0
  73. package/treb-embed/markup/layout.html +137 -0
  74. package/treb-embed/markup/toolbar.html +175 -0
  75. package/treb-embed/modern.tsconfig.json +25 -0
  76. package/treb-embed/src/custom-element/content-types.d.ts +18 -0
  77. package/treb-embed/src/custom-element/global.d.ts +11 -0
  78. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1227 -0
  79. package/treb-embed/src/custom-element/treb-global.ts +44 -0
  80. package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
  81. package/treb-embed/src/embedded-spreadsheet.ts +5362 -0
  82. package/treb-embed/src/index.ts +16 -0
  83. package/treb-embed/src/language-model.ts +41 -0
  84. package/treb-embed/src/options.ts +320 -0
  85. package/treb-embed/src/progress-dialog.ts +228 -0
  86. package/treb-embed/src/selection-state.ts +16 -0
  87. package/treb-embed/src/spinner.ts +42 -0
  88. package/treb-embed/src/toolbar-message.ts +96 -0
  89. package/treb-embed/src/types.ts +167 -0
  90. package/treb-embed/style/autocomplete.scss +103 -0
  91. package/treb-embed/style/dark-theme.scss +114 -0
  92. package/treb-embed/style/defaults.scss +36 -0
  93. package/treb-embed/style/dialog.scss +181 -0
  94. package/treb-embed/style/dropdown-select.scss +101 -0
  95. package/treb-embed/style/formula-bar.scss +193 -0
  96. package/treb-embed/style/grid.scss +374 -0
  97. package/treb-embed/style/layout.scss +424 -0
  98. package/treb-embed/style/mouse-mask.scss +67 -0
  99. package/treb-embed/style/note.scss +92 -0
  100. package/treb-embed/style/overlay-editor.scss +102 -0
  101. package/treb-embed/style/spinner.scss +92 -0
  102. package/treb-embed/style/tab-bar.scss +228 -0
  103. package/treb-embed/style/table.scss +80 -0
  104. package/treb-embed/style/theme-defaults.scss +444 -0
  105. package/treb-embed/style/toolbar.scss +416 -0
  106. package/treb-embed/style/tooltip.scss +68 -0
  107. package/treb-embed/style/treb-icons.scss +130 -0
  108. package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
  109. package/treb-embed/style/z-index.scss +43 -0
  110. package/treb-export/docs/charts.md +68 -0
  111. package/treb-export/modern.tsconfig.json +19 -0
  112. package/treb-export/package.json +4 -0
  113. package/treb-export/src/address-type.ts +77 -0
  114. package/treb-export/src/base-template.ts +22 -0
  115. package/treb-export/src/column-width.ts +85 -0
  116. package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
  117. package/treb-export/src/drawing2/chart2.ts +282 -0
  118. package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
  119. package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
  120. package/treb-export/src/drawing2/drawing2.ts +355 -0
  121. package/treb-export/src/drawing2/embedded-image.ts +71 -0
  122. package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
  123. package/treb-export/src/export-worker/export-worker.ts +99 -0
  124. package/treb-export/src/export-worker/index-modern.ts +22 -0
  125. package/treb-export/src/export2.ts +2204 -0
  126. package/treb-export/src/import2.ts +882 -0
  127. package/treb-export/src/relationship.ts +36 -0
  128. package/treb-export/src/shared-strings2.ts +128 -0
  129. package/treb-export/src/template-2.ts +22 -0
  130. package/treb-export/src/unescape_xml.ts +47 -0
  131. package/treb-export/src/workbook-sheet2.ts +182 -0
  132. package/treb-export/src/workbook-style2.ts +1285 -0
  133. package/treb-export/src/workbook-theme2.ts +88 -0
  134. package/treb-export/src/workbook2.ts +491 -0
  135. package/treb-export/src/xml-utils.ts +201 -0
  136. package/treb-export/template/base/[Content_Types].xml +2 -0
  137. package/treb-export/template/base/_rels/.rels +2 -0
  138. package/treb-export/template/base/docProps/app.xml +2 -0
  139. package/treb-export/template/base/docProps/core.xml +12 -0
  140. package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
  141. package/treb-export/template/base/xl/sharedStrings.xml +2 -0
  142. package/treb-export/template/base/xl/styles.xml +2 -0
  143. package/treb-export/template/base/xl/theme/theme1.xml +2 -0
  144. package/treb-export/template/base/xl/workbook.xml +2 -0
  145. package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
  146. package/treb-export/template/base.xlsx +0 -0
  147. package/treb-format/package.json +8 -0
  148. package/treb-format/src/format.test.ts +213 -0
  149. package/treb-format/src/format.ts +942 -0
  150. package/treb-format/src/format_cache.ts +199 -0
  151. package/treb-format/src/format_parser.ts +723 -0
  152. package/treb-format/src/index.ts +25 -0
  153. package/treb-format/src/number_format_section.ts +100 -0
  154. package/treb-format/src/value_parser.ts +337 -0
  155. package/treb-grid/package.json +5 -0
  156. package/treb-grid/src/editors/autocomplete.ts +394 -0
  157. package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
  158. package/treb-grid/src/editors/formula_bar.ts +473 -0
  159. package/treb-grid/src/editors/formula_editor_base.ts +910 -0
  160. package/treb-grid/src/editors/overlay_editor.ts +511 -0
  161. package/treb-grid/src/index.ts +37 -0
  162. package/treb-grid/src/layout/base_layout.ts +2618 -0
  163. package/treb-grid/src/layout/grid_layout.ts +299 -0
  164. package/treb-grid/src/layout/rectangle_cache.ts +86 -0
  165. package/treb-grid/src/render/selection-renderer.ts +414 -0
  166. package/treb-grid/src/render/svg_header_overlay.ts +93 -0
  167. package/treb-grid/src/render/svg_selection_block.ts +187 -0
  168. package/treb-grid/src/render/tile_renderer.ts +2122 -0
  169. package/treb-grid/src/types/annotation.ts +216 -0
  170. package/treb-grid/src/types/border_constants.ts +34 -0
  171. package/treb-grid/src/types/clipboard_data.ts +31 -0
  172. package/treb-grid/src/types/data_model.ts +334 -0
  173. package/treb-grid/src/types/drag_mask.ts +81 -0
  174. package/treb-grid/src/types/grid.ts +7743 -0
  175. package/treb-grid/src/types/grid_base.ts +3644 -0
  176. package/treb-grid/src/types/grid_command.ts +470 -0
  177. package/treb-grid/src/types/grid_events.ts +124 -0
  178. package/treb-grid/src/types/grid_options.ts +97 -0
  179. package/treb-grid/src/types/grid_selection.ts +60 -0
  180. package/treb-grid/src/types/named_range.ts +369 -0
  181. package/treb-grid/src/types/scale-control.ts +202 -0
  182. package/treb-grid/src/types/serialize_options.ts +72 -0
  183. package/treb-grid/src/types/set_range_options.ts +52 -0
  184. package/treb-grid/src/types/sheet.ts +3099 -0
  185. package/treb-grid/src/types/sheet_types.ts +95 -0
  186. package/treb-grid/src/types/tab_bar.ts +464 -0
  187. package/treb-grid/src/types/tile.ts +59 -0
  188. package/treb-grid/src/types/update_flags.ts +75 -0
  189. package/treb-grid/src/util/dom_utilities.ts +44 -0
  190. package/treb-grid/src/util/fontmetrics2.ts +179 -0
  191. package/treb-grid/src/util/ua.ts +104 -0
  192. package/treb-logo.svg +18 -0
  193. package/treb-parser/package.json +5 -0
  194. package/treb-parser/src/csv-parser.ts +122 -0
  195. package/treb-parser/src/index.ts +25 -0
  196. package/treb-parser/src/md-parser.ts +526 -0
  197. package/treb-parser/src/parser-types.ts +397 -0
  198. package/treb-parser/src/parser.test.ts +298 -0
  199. package/treb-parser/src/parser.ts +2673 -0
  200. package/treb-utils/package.json +5 -0
  201. package/treb-utils/src/dispatch.ts +57 -0
  202. package/treb-utils/src/event_source.ts +147 -0
  203. package/treb-utils/src/ievent_source.ts +33 -0
  204. package/treb-utils/src/index.ts +31 -0
  205. package/treb-utils/src/measurement.ts +174 -0
  206. package/treb-utils/src/resizable.ts +160 -0
  207. package/treb-utils/src/scale.ts +137 -0
  208. package/treb-utils/src/serialize_html.ts +124 -0
  209. package/treb-utils/src/template.ts +70 -0
  210. package/treb-utils/src/validate_uri.ts +61 -0
  211. package/tsconfig.json +10 -0
  212. package/tsproject.json +30 -0
  213. package/util/license-plugin-esbuild.js +86 -0
  214. package/util/list-css-vars.sh +46 -0
  215. package/README-esm.md +0 -37
  216. package/treb-bundle.css +0 -2
  217. package/treb-bundle.mjs +0 -15
@@ -0,0 +1,1069 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import type { FunctionLibrary } from './function-library';
23
+ import { Cell, ICellAddress, ValueType, GetValueType,
24
+ Area, UnionValue, CellValue,
25
+ ArrayUnion,
26
+ NumberUnion,
27
+ UndefinedUnion,
28
+ ComplexUnion } from 'treb-base-types';
29
+ import type { Parser, ExpressionUnit, UnitBinary, UnitIdentifier,
30
+ UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference } from 'treb-parser';
31
+ import type { DataModel, MacroFunction, Sheet } from 'treb-grid';
32
+ import { NameError, ReferenceError, ExpressionError, UnknownError } from './function-error';
33
+ import { ReturnType } from './descriptors';
34
+
35
+ import * as Primitives from './primitives';
36
+
37
+ export type ExtendedExpressionUnit = ExpressionUnit & { user_data: any }; // export for MC overload
38
+
39
+ // FIXME: move
40
+ export const UnionIsExpressionUnit = (test: UnionValue /*UnionOrArray*/): test is { type: ValueType.object, value: ExpressionUnit } => {
41
+ return !Array.isArray(test)
42
+ && test.type === ValueType.object
43
+ && (!!(test.value as ExpressionUnit).type);
44
+ };
45
+
46
+ // FIXME: move
47
+ export const UnionIsMetadata = (test: UnionValue /*UnionOrArray*/): test is { type: ValueType.object, value: ReferenceMetadata } => {
48
+
49
+ return test.type === ValueType.object && test.key === 'metadata';
50
+
51
+ /*
52
+ return !Array.isArray(test)
53
+ && test.type === ValueType.object
54
+ && ((test.value as ReferenceMetadata).type === 'metadata');
55
+ */
56
+ };
57
+
58
+ // FIXME: move
59
+ export interface ReferenceMetadata {
60
+ type: 'metadata';
61
+ address: UnitAddress; // ICellAddress;
62
+ value: CellValue;
63
+ format?: string;
64
+ }
65
+
66
+ export interface CalculationContext {
67
+ address: ICellAddress;
68
+ model?: DataModel;
69
+ volatile: boolean;
70
+ call_index: number;
71
+ }
72
+
73
+ export class ExpressionCalculator {
74
+
75
+ public context: CalculationContext = {
76
+ address: { row: -1, column: -1 },
77
+ volatile: false,
78
+ call_index: 0,
79
+ };
80
+
81
+ /**
82
+ * this refers to the number of function call within a single cell.
83
+ * so if you have a function like
84
+ *
85
+ * =A(B())
86
+ *
87
+ * then when calculating A call index should be set to 1; and when
88
+ * calculating B, call index is 2. and so on.
89
+ */
90
+ protected call_index = 0;
91
+
92
+ // local reference
93
+ // protected cells: Cells = new Cells();
94
+ // protected cells_map: {[index: number]: Cells} = {};
95
+ // protected sheet_name_map: {[index: string]: number} = {};
96
+
97
+ // local reference
98
+ protected named_range_map: {[index: string]: Area} = {};
99
+
100
+ // protected bound_name_stack: Array<Record<string, ExpressionUnit>> = [];
101
+
102
+ //
103
+ protected data_model!: DataModel;
104
+
105
+
106
+ // --- public API -----------------------------------------------------------
107
+
108
+ constructor(
109
+ protected readonly library: FunctionLibrary,
110
+ protected readonly parser: Parser) {}
111
+
112
+ public SetModel(model: DataModel): void {
113
+
114
+ // this.cells_map = {};
115
+ // this.sheet_name_map = {};
116
+
117
+ /*
118
+ for (const sheet of model.sheets.list) {
119
+ // this.cells_map[sheet.id] = sheet.cells;
120
+ // this.sheet_name_map[sheet.name.toLowerCase()] = sheet.id;
121
+ }
122
+ */
123
+
124
+ this.data_model = model;
125
+ this.named_range_map = model.named_ranges.Map();
126
+ this.context.model = model;
127
+
128
+ }
129
+
130
+ /**
131
+ * there's a case where we are calling this from within a function
132
+ * (which is weird, but hey) and to do that we need to preserve flags.
133
+ */
134
+ public Calculate(expr: ExpressionUnit, addr: ICellAddress, preserve_flags = false): {
135
+ value: UnionValue /*UnionOrArray*/, volatile: boolean }{
136
+
137
+ if (!preserve_flags) {
138
+
139
+ this.context.address = addr;
140
+ this.context.volatile = false;
141
+ this.context.call_index = 0;
142
+
143
+ // reset for this cell
144
+ this.call_index = 0; // why not in model? A: timing (nested)
145
+
146
+ }
147
+
148
+ return {
149
+ value: this.CalculateExpression(expr as ExtendedExpressionUnit),
150
+ volatile: this.context.volatile,
151
+ };
152
+
153
+ }
154
+
155
+ // --- /public API ----------------------------------------------------------
156
+
157
+ /**
158
+ * resolve value from cell. returns a function bound to specific cell.
159
+ */
160
+ protected CellFunction2(expr: UnitAddress): () => UnionValue {
161
+
162
+ if (!expr.sheet_id) {
163
+ if (expr.sheet) {
164
+ expr.sheet_id = this.data_model.sheets.ID(expr.sheet) || 0;
165
+
166
+ // expr.sheet_id = this.sheet_name_map[expr.sheet.toLowerCase()];
167
+ }
168
+ else {
169
+ return () => ReferenceError();
170
+ }
171
+ }
172
+
173
+ // const cells = this.cells_map[expr.sheet_id];
174
+ const cells = this.data_model.sheets.Find(expr.sheet_id)?.cells;
175
+
176
+ if (!cells) {
177
+ console.warn('missing cells reference @ ' + expr.sheet_id);
178
+ return () => ReferenceError();
179
+ }
180
+
181
+ // reference
182
+ const cell = cells.GetCell(expr);
183
+
184
+ // this is not an error, just a reference to an empty cell
185
+ // FIXME: should this be 0? probably
186
+
187
+ if (!cell) {
188
+ return () => {
189
+ // return { type: ValueType.undefined, value: undefined }
190
+ return { type: ValueType.number, value: 0 };
191
+ };
192
+ }
193
+
194
+ // close
195
+ return () => cell.GetValue4();
196
+
197
+ }
198
+
199
+ /**
200
+ * returns range as union type. returns a single value for a single cell,
201
+ * or a 2d array (never a 1d array)
202
+ */
203
+ protected CellFunction4(start: ICellAddress, end: ICellAddress): UnionValue /*UnionOrArray*/ {
204
+
205
+ if (!start.sheet_id) {
206
+ return ReferenceError();
207
+ // throw new Error('missing sheet id in CellFunction4');
208
+ }
209
+
210
+ //const cells = this.cells_map[start.sheet_id];
211
+ const cells = this.data_model.sheets.Find(start.sheet_id)?.cells;
212
+
213
+ return cells?.GetRange4(start, end, true) || ReferenceError();
214
+
215
+ }
216
+
217
+ /** breaking this out to de-dupe */
218
+ protected GetMetadata(arg: ExpressionUnit, map_result: (cell_data: Cell, address: ICellAddress) => any): UnionValue /*UnionOrArray*/ {
219
+
220
+ // FIXME: we used to restrict this to non-cell functions, now
221
+ // we are using it for the cell function (we used to use address,
222
+ // which just returns the label)
223
+
224
+ let address: ICellAddress|undefined;
225
+ let range: {start: ICellAddress; end: ICellAddress} | undefined;
226
+
227
+ switch (arg.type) {
228
+ case 'address':
229
+ address = arg;
230
+ break;
231
+
232
+ case 'range':
233
+ range = arg;
234
+ break;
235
+
236
+ case 'structured-reference':
237
+ {
238
+ const resolved = this.data_model.ResolveStructuredReference(arg, this.context.address);
239
+ if (resolved) {
240
+ if (resolved.type === 'address') {
241
+ address = resolved;
242
+ }
243
+ else if (resolved.type === 'range') {
244
+ range = resolved;
245
+ }
246
+ }
247
+ }
248
+ break;
249
+
250
+ case 'identifier':
251
+ {
252
+ const named_range = this.named_range_map[arg.name.toUpperCase()];
253
+ if (named_range) {
254
+ if (named_range.count === 1) {
255
+ address = named_range.start; // FIXME: range?
256
+ }
257
+ else {
258
+ range = named_range;
259
+ }
260
+ }
261
+ }
262
+ break;
263
+
264
+ case 'call':
265
+
266
+ // we need a way to cascade the 'metadata' flag down
267
+ // through calls so we can use indirect/offset addressing...
268
+
269
+ // at the same time you don't want to cascade down indefinitely,
270
+ // otherwise the function call itself won't work properly...
271
+
272
+ // [how to resolve?]
273
+
274
+ {
275
+ const result = this.CalculateExpression(arg as ExtendedExpressionUnit, true) as UnionValue /*UnionOrArray*/;
276
+ if (UnionIsExpressionUnit(result)) {
277
+ if (result.value.type === 'address') {
278
+ address = result.value;
279
+ }
280
+ else if (result.value.type === 'range') {
281
+ range = result.value;
282
+ }
283
+ else {
284
+ return result;
285
+ }
286
+ }
287
+ else return result;
288
+ }
289
+ break;
290
+
291
+ default:
292
+ return this.CalculateExpression(arg as ExtendedExpressionUnit); // as UnionOrArray;
293
+
294
+ }
295
+
296
+ if (address) {
297
+
298
+ // don't we have a map? [...] only for names?
299
+
300
+ let sheet: Sheet|undefined; // = this.data_model.active_sheet;
301
+ if (address.sheet_id) { // && address.sheet_id !== sheet.id) {
302
+ sheet = this.data_model.sheets.Find(address.sheet_id);
303
+
304
+ /*
305
+ for (const test of this.data_model.sheets) {
306
+ if (test.id === address.sheet_id) {
307
+ sheet = test;
308
+ break;
309
+ }
310
+ }
311
+ */
312
+ }
313
+
314
+ if (!sheet) {
315
+ // throw new Error('missing sheet [ac8]');
316
+ console.error('missing sheet [ac8]');
317
+ return ReferenceError();
318
+ }
319
+
320
+ const cell_data = sheet.CellData(address);
321
+ const value = // (cell_data.type === ValueType.formula) ? cell_data.calculated : cell_data.value;
322
+ cell_data.calculated_type ? cell_data.calculated : cell_data.value;
323
+
324
+ const metadata: ReferenceMetadata = {
325
+ type: 'metadata',
326
+ address: {...address},
327
+ value,
328
+ format: cell_data.style ? cell_data.style.number_format : undefined,
329
+ ...map_result(cell_data, address),
330
+ };
331
+
332
+ return { type: ValueType.object, value: metadata, key: 'metadata' };
333
+
334
+ }
335
+ else if (range) {
336
+
337
+ if (range.start.row === Infinity || range.start.column === Infinity) {
338
+ return ReferenceError();
339
+ }
340
+
341
+ let sheet: Sheet|undefined; // = this.data_model.active_sheet;
342
+ if (range.start.sheet_id) { // && range.start.sheet_id !== sheet.id) {
343
+ sheet = this.data_model.sheets.Find(range.start.sheet_id);
344
+ /*
345
+ for (const test of this.data_model.sheets) {
346
+ if (test.id === range.start.sheet_id) {
347
+ sheet = test;
348
+ break;
349
+ }
350
+ }
351
+ */
352
+ }
353
+
354
+ if (!sheet) {
355
+ // console.info({range, context: JSON.stringify(this.context.address)});
356
+ // console.info({arg});
357
+ throw new Error('missing sheet [ac9]');
358
+ }
359
+
360
+ const range_result: UnionValue[][] = [];
361
+
362
+ for (let column = range.start.column; column <= range.end.column; column++) {
363
+ const column_result: UnionValue[] = [];
364
+ for (let row = range.start.row; row <= range.end.row; row++) {
365
+ const cell_data = sheet.CellData({row, column});
366
+ address = {...range.start, row, column};
367
+
368
+ const value = // (cell_data.type === ValueType.formula) ? cell_data.calculated : cell_data.value;
369
+ cell_data.calculated_type ? cell_data.calculated : cell_data.value;
370
+
371
+ const metadata = {
372
+ type: 'metadata',
373
+ address,
374
+ value,
375
+ format: cell_data.style ? cell_data.style.number_format : undefined,
376
+ ...map_result(cell_data, address),
377
+ };
378
+
379
+ column_result.push({
380
+ type: ValueType.object,
381
+ value: metadata,
382
+ key: 'metadata',
383
+ });
384
+
385
+ }
386
+ range_result.push(column_result);
387
+ }
388
+
389
+ return {type: ValueType.array, value: range_result};
390
+
391
+ }
392
+
393
+ return this.CalculateExpression(arg as ExtendedExpressionUnit); /*UnionOrArray*/
394
+
395
+ }
396
+
397
+ protected RewriteMacro(
398
+ unit: ExpressionUnit,
399
+ names: Record<string, ExpressionUnit>,
400
+ ): ExpressionUnit {
401
+
402
+ let expr: ExpressionUnit;
403
+
404
+ switch (unit.type) {
405
+
406
+ case 'identifier':
407
+ expr = names[unit.name.toUpperCase()];
408
+ if (expr) {
409
+ return JSON.parse(JSON.stringify(expr)) as ExpressionUnit;
410
+ }
411
+ break;
412
+
413
+ case 'binary':
414
+ unit.left = this.RewriteMacro(unit.left, names);
415
+ unit.right = this.RewriteMacro(unit.right, names);
416
+ break;
417
+
418
+ case 'unary':
419
+ unit.operand = this.RewriteMacro(unit.operand, names);
420
+ break;
421
+
422
+ case 'group':
423
+ unit.elements = unit.elements.map(element => this.RewriteMacro(element, names));
424
+ break;
425
+
426
+ case 'call':
427
+ unit.args = unit.args.map(arg => this.RewriteMacro(arg, names));
428
+ break;
429
+
430
+ }
431
+
432
+ return unit;
433
+
434
+ }
435
+
436
+ protected CallMacro(outer: UnitCall, macro: MacroFunction): (expr: UnitCall) => UnionValue /*UnionOrArray*/ {
437
+
438
+ if (!macro.expression) {
439
+ return () => ExpressionError();
440
+ }
441
+ const text_expr = JSON.stringify(macro.expression);
442
+ const names: Record<string, ExpressionUnit> = {};
443
+ const upper_case_names = macro.argument_names?.map(name => name.toUpperCase()) || [];
444
+
445
+ return (expr: UnitCall) => {
446
+
447
+ const clone = JSON.parse(text_expr);
448
+
449
+ for (let i = 0; i < upper_case_names.length; i++) {
450
+ names[upper_case_names[i]] = expr.args[i] || { type: 'missing', id: 0 };
451
+ }
452
+
453
+ return this.CalculateExpression(this.RewriteMacro(clone, names) as ExtendedExpressionUnit);
454
+
455
+ }
456
+
457
+ }
458
+
459
+ /**
460
+ * excutes a function call
461
+ *
462
+ * the return type of functions has never been locked down, and as a result
463
+ * there are a couple of things we need to handle.
464
+ *
465
+ * return type can be any value, essentially, or array, error object, or
466
+ * (in the case of some of the reference/lookup functions) an address or
467
+ * range expression. array must be 2d, I think? not sure that that is true.
468
+ *
469
+ * this wrapper function returns a function which returns one of those
470
+ * things, i.e. it returns (expr) => return type
471
+ *
472
+ * it will only return address/range if the parameter flag is set, so we
473
+ * could in theory lock it down a bit with overloads.
474
+ *
475
+ * ---
476
+ *
477
+ * UPDATE: that's no longer the case. we require that functions return
478
+ * a UnionValue type (union), which can itself contain an array.
479
+ *
480
+ * ---
481
+ *
482
+ * FIXME: there is far too much duplication between this and the MC version
483
+ * (in simulation-expression-calculator). we need to find a way to consolidate
484
+ * these.
485
+ *
486
+ * I think the problem is that we don't want a lot of switches, but the cost
487
+ * is an almost complete duplicate of this function in the subclass.
488
+ *
489
+ */
490
+ protected CallExpression(outer: UnitCall, return_reference = false): (expr: UnitCall) => UnionValue /*UnionOrArray*/ {
491
+
492
+ // get the function descriptor, which won't change.
493
+ // we can bind in closure (also short-circuit check for
494
+ // invalid name)
495
+
496
+ const func = this.library.Get(outer.name);
497
+
498
+ if (!func) {
499
+ return () => NameError();
500
+ }
501
+
502
+ return (expr: UnitCall) => {
503
+
504
+ // get an index we can use for this call (we may recurse when
505
+ // calculating arguments), then increment for the next call.
506
+
507
+ const call_index = this.call_index++;
508
+
509
+ // yeah so this is clear. just checking volatile.
510
+
511
+ // FIXME: should this be set later, at the same time as the
512
+ // calculation index? I think it should, since we may recurse.
513
+
514
+ // BEFORE YOU DO THAT, track down all references that read this field
515
+
516
+ // from what I can tell, the only place this is read is after the
517
+ // external (outer) Calculate() call. so we should move this assignment,
518
+ // and we should also be able to get it to fail:
519
+ //
520
+ // RandBetween() should be volatile, but if we have a nonvolatile function
521
+ // as an argument that should unset it, and remove the volatile flag.
522
+ // Check?
523
+
524
+ // actually this works, because it only sets the flag (does not unset).
525
+ // volatile applies to the _cell_, not just the function -- so as long
526
+ // as the outer function sets the flag, it's not material if an inner
527
+ // function is nonvolatile. similarly an inner volatile function will
528
+ // make the outer function volatile.
529
+
530
+ // this does mean that the nonvolatile function will be treated differently
531
+ // if it's an argument to a volatile function, but I think that's reasonable
532
+ // behavior; also it's symmetric with the opposite case (inner volatile.)
533
+
534
+ // so leave this as-is, or you can move it -- should be immaterial
535
+
536
+ this.context.volatile = this.context.volatile || (!!func.volatile);
537
+
538
+ // NOTE: the argument logic is (possibly) calculating unecessary operations,
539
+ // if there's a conditional (like an IF function). although that is the
540
+ // exception rather than the rule...
541
+
542
+ // ok we can handle IF functions, at the expense of some tests...
543
+ // is it worth it?
544
+
545
+ const if_function = outer.name.toLowerCase() === 'if';
546
+ let skip_argument_index = -1;
547
+
548
+ let argument_error: UnionValue|undefined;
549
+
550
+ const argument_descriptors = func.arguments || []; // map
551
+
552
+ const mapped_args = expr.args.map((arg, arg_index) => {
553
+
554
+ // short circuit
555
+ if (argument_error) {
556
+ return undefined;
557
+ }
558
+
559
+ // get descriptor. if the number of arguments exceeds
560
+ // the number of descriptors, recycle the last one
561
+ const descriptor = argument_descriptors[Math.min(arg_index, argument_descriptors.length - 1)] || {};
562
+
563
+ // if function, wrong branch
564
+ if (arg_index === skip_argument_index) {
565
+ return descriptor.boxed ? { type: ValueType.undefined } : undefined;
566
+ }
567
+
568
+ // note on type here: we're iterating over the arguments
569
+ // described by the parse expression, not the values. although
570
+ // in this case, wouldn't this be a missing type? (...)
571
+ if (typeof arg === 'undefined') {
572
+ if (if_function && arg_index === 0) { skip_argument_index = 1; }
573
+ return descriptor.boxed ? { type: ValueType.undefined } : undefined;
574
+ }
575
+
576
+ // FIXME (address): what about named ranges (actually those will work),
577
+ // constructed references (we don't support them atm)?
578
+
579
+ // NOTE: named ranges will _not_ work, because the address will be an
580
+ // object, not a string. so FIXME.
581
+
582
+ if (descriptor.address) {
583
+ return descriptor.boxed ? {
584
+ type: ValueType.string,
585
+ value: this.parser.Render(arg).replace(/\$/g, ''),
586
+ } : this.parser.Render(arg).replace(/\$/g, '');
587
+ }
588
+ else if (descriptor.metadata) {
589
+
590
+ return this.GetMetadata(arg, () => { return {}}); // type is UnionOrArray
591
+
592
+ }
593
+ else {
594
+
595
+ const result = this.CalculateExpression(arg as ExtendedExpressionUnit);
596
+
597
+ // if (!Array.isArray(result) && result.type === ValueType.error) {
598
+
599
+ if (result.type === ValueType.error) { // array check is implicit since array is a type
600
+ if (descriptor.allow_error) {
601
+ return result; // always boxed
602
+ }
603
+ argument_error = result;
604
+ return undefined; // argument not used, so don't bother boxing
605
+ }
606
+
607
+ // can't shortcut if you have an array (or we need to test all the values)
608
+
609
+ //if (if_function && arg_index === 0 && !Array.isArray(result)) {
610
+ if (if_function && arg_index === 0 && result.type !== ValueType.array){ // !Array.isArray(result)) {
611
+ let result_truthy = false;
612
+
613
+ // if (Array.isArray(result)) { result_truthy = true; }
614
+
615
+ if (result.type === ValueType.string) {
616
+ const lowercase = (result.value as string).toLowerCase().trim();
617
+ result_truthy = lowercase !== 'false' && lowercase !== 'f';
618
+ }
619
+ else {
620
+ result_truthy = !!result.value;
621
+ }
622
+ skip_argument_index = result_truthy ? 2 : 1;
623
+ }
624
+
625
+ if (descriptor.boxed) {
626
+ return result;
627
+ }
628
+
629
+ /*
630
+ if (Array.isArray(result)) {
631
+ return result.map(row => row.map(value => value.value));
632
+ }
633
+ */
634
+ if (result.type === ValueType.array) {
635
+ return (result as ArrayUnion).value.map(row => row.map(value => value.value));
636
+ }
637
+ else {
638
+ return result.value; // unboxing
639
+ }
640
+
641
+ }
642
+
643
+ });
644
+
645
+ if (argument_error) {
646
+ return argument_error;
647
+ }
648
+
649
+ // if we have any nested calls, they may have updated the index so
650
+ // we use the captured value here.
651
+
652
+ this.context.call_index = call_index;
653
+
654
+ // I thought we were passing the model as this (...) ? actually
655
+ // now we bind functions that need this, so maybe we should pass
656
+ // null here.
657
+
658
+ // return func.fn.apply(null, mapped_args);
659
+
660
+ if (func.return_type === ReturnType.reference) {
661
+
662
+ const result = func.fn.apply(null, mapped_args);
663
+
664
+ if (return_reference) {
665
+ return result;
666
+ }
667
+
668
+ if (UnionIsExpressionUnit(result)) {
669
+ if (result.value.type === 'address') {
670
+ return this.CellFunction2(result.value)();
671
+ }
672
+ else if (result.value.type === 'range') {
673
+ return this.CellFunction4(result.value.start, result.value.end)
674
+ }
675
+ }
676
+
677
+ return result; // error?
678
+
679
+ }
680
+
681
+ return func.fn.apply(null, mapped_args);
682
+
683
+ };
684
+
685
+ }
686
+
687
+ protected ResolveStructuredReference(expr: UnitStructuredReference): () => UnionValue {
688
+
689
+ // basically our approach here is to resolve the structured reference
690
+ // to a concrete reference.
691
+ //
692
+ // if the structured reference changes, then it will get recalculated
693
+ // (and hence rebuilt). if the table name or a referenced column name
694
+ // changes, the cell will get rewritten so again, it will get recalculated.
695
+ //
696
+ // the case we have to worry about is if the table layout changes: if a
697
+ // column is added or removed. because in that case, our reference will
698
+ // be out of date but we won't be notified about it.
699
+ //
700
+ // so we will have to make sure that if a table layout changes, columns
701
+ // or rows added or deleted, then we invalidate the entire table. if we
702
+ // do that this should all work out.
703
+
704
+ const resolved = this.data_model.ResolveStructuredReference(expr, this.context.address);
705
+ if (resolved) {
706
+ if (resolved.type === 'address') {
707
+ return this.CellFunction2(resolved);
708
+ }
709
+ else if(resolved.type === 'range') {
710
+ return () => this.CellFunction4(resolved.start, resolved.end);
711
+ }
712
+ }
713
+
714
+ return () => ReferenceError();
715
+
716
+ }
717
+
718
+ protected ResolveDimensionedQuantity(): (exp: UnitDimensionedQuantity) => UnionValue {
719
+
720
+ return (expr: UnitDimensionedQuantity): UnionValue => {
721
+ const expression = this.CalculateExpression(expr.expression as ExtendedExpressionUnit);
722
+ return {
723
+ type: ValueType.dimensioned_quantity,
724
+ value: {
725
+ value: expression.value,
726
+ unit: expr.unit.name,
727
+ },
728
+ };
729
+ };
730
+
731
+ }
732
+
733
+ protected UnaryExpression(x: UnitUnary): (expr: UnitUnary) => UnionValue /*UnionOrArray*/ { // operator: string, operand: any){
734
+
735
+ // there are basically three code paths here: negate, identity, and error.
736
+ // they have very different semantics so we're going to do them completely
737
+ // separately.
738
+
739
+ switch (x.operator) {
740
+ case '+':
741
+ return (expr: UnitUnary) => {
742
+ return this.CalculateExpression(expr.operand as ExtendedExpressionUnit);
743
+ };
744
+
745
+ case '-':
746
+ {
747
+ const func = Primitives.Subtract;
748
+ const zero = { type: ValueType.number, value: 0 } as NumberUnion;
749
+
750
+ return (expr: UnitUnary) => {
751
+ const operand = this.CalculateExpression(expr.operand as ExtendedExpressionUnit);
752
+ if (operand.type === ValueType.array) {
753
+ return {
754
+ type: ValueType.array,
755
+ value: (operand as ArrayUnion).value.map(column => column.map(value => func(zero, value))),
756
+ };
757
+ }
758
+ /*
759
+ if (Array.isArray(operand)) {
760
+ return operand.map(column => column.map(value => func(zero, value)));
761
+ }
762
+ */
763
+ return func(zero, operand);
764
+ };
765
+
766
+ }
767
+
768
+ default:
769
+ return () => {
770
+ console.warn('unexpected unary operator:', x.operator);
771
+ return ExpressionError();
772
+ };
773
+ }
774
+
775
+ }
776
+
777
+ /**
778
+ * expands the size of an array by recycling values in columns and rows
779
+ *
780
+ * FIXME: seems like this is more a generic thing, -> utils lib
781
+ *
782
+ * @param arr 2d array
783
+ * @param columns target columns
784
+ * @param rows target rows
785
+ */
786
+ protected RecycleArray<T>(arr: T[][], columns: number, rows: number): T[][] {
787
+
788
+ // NOTE: recycle rows first, more efficient. do it in place?
789
+
790
+ if (arr[0].length < rows) {
791
+ const len = arr[0].length;
792
+ for (const column of arr) {
793
+ for (let r = len; r < rows; r++ ) {
794
+ column[r] = column[r % len];
795
+ }
796
+ }
797
+ }
798
+
799
+ if (arr.length < columns) {
800
+ const len = arr.length;
801
+ for (let c = len; c < columns; c++) arr[c] = arr[c % len].slice(0);
802
+ }
803
+
804
+ return arr;
805
+
806
+ }
807
+
808
+ //protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: UnionValue[][], right: UnionValue[][]): UnionValue[][] {
809
+ protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: ArrayUnion, right: ArrayUnion): ArrayUnion {
810
+
811
+ const columns = Math.max(left.value.length, right.value.length);
812
+ const rows = Math.max(left.value[0].length, right.value[0].length);
813
+
814
+ // const columns = Math.max(left.length, right.length);
815
+ // const rows = Math.max(left[0].length, right[0].length);
816
+
817
+ const left_values = this.RecycleArray(left.value, columns, rows);
818
+ const right_values = this.RecycleArray(right.value, columns, rows);
819
+
820
+ const value: UnionValue[][] = [];
821
+
822
+ for (let c = 0; c < columns; c++) {
823
+ const col = [];
824
+ for (let r = 0; r < rows; r++ ) {
825
+ col[r] = fn(left_values[c][r], right_values[c][r]);
826
+ }
827
+ value.push(col);
828
+ }
829
+
830
+ return { type: ValueType.array, value };
831
+
832
+ }
833
+
834
+ protected BinaryExpression(x: UnitBinary): (expr: UnitBinary) => UnionValue /*UnionOrArray*/ {
835
+
836
+ // we are constructing and caching functions for binary expressions.
837
+ // this should simplify calls when parameters change. eventually I'd
838
+ // like to do this for other dynamic calls as well...
839
+
840
+ // the idea is that we can start composing compound expressions. still
841
+ // not sure if that will work (or if it's a good idea).
842
+
843
+ // NOTE (for the future?) if one or both of the operands is a literal,
844
+ // we can bind that directly. literals in the expression won't change
845
+ // unless the expression changes, which will discard the generated
846
+ // function (along with the expression itself).
847
+
848
+ const fn = Primitives.MapOperator(x.operator);
849
+
850
+ if (!fn) {
851
+ return () => { // expr: UnitBinary) => {
852
+ console.info(`(unexpected binary operator: ${x.operator})`);
853
+ return ExpressionError();
854
+ };
855
+ }
856
+ else {
857
+ return (expr: UnitBinary) => {
858
+
859
+ // sloppy typing, to support operators? (...)
860
+
861
+ const left = this.CalculateExpression(expr.left as ExtendedExpressionUnit);
862
+ const right = this.CalculateExpression(expr.right as ExtendedExpressionUnit);
863
+
864
+ // check for arrays. do elementwise operations.
865
+
866
+ if (left.type === ValueType.array) {
867
+ if (right.type === ValueType.array) {
868
+ return this.ElementwiseBinaryExpression(fn, left as ArrayUnion, right as ArrayUnion);
869
+ }
870
+ return this.ElementwiseBinaryExpression(fn, left as ArrayUnion, {type: ValueType.array, value: [[right]]});
871
+ }
872
+ else if (right.type === ValueType.array) {
873
+ return this.ElementwiseBinaryExpression(fn, {type: ValueType.array, value: [[left]]}, right as ArrayUnion);
874
+ }
875
+
876
+ return fn(left, right);
877
+
878
+ };
879
+ }
880
+
881
+ }
882
+
883
+ protected Identifier(expr: UnitIdentifier): () => UnionValue /*UnionOrArray*/ {
884
+
885
+ // NOTE: TRUE and FALSE don't get here -- they are converted
886
+ // to literals by the parser? (...)
887
+
888
+ // the function we create here binds the name because
889
+ // this is a literal identifier. if the value were to change,
890
+ // the expression would be discarded.
891
+
892
+ // however we have to do the lookup dynamically because the
893
+ // underlying reference (in the named range map) might change.
894
+
895
+ // although it's worth noting that, atm at least, that wouldn't
896
+ // trigger an update because it's not considered a value change.
897
+ // you'd have to recalc, which would rebuild the expression anyway.
898
+ // call that a FIXME? (...)
899
+
900
+ const identifier = expr.name;
901
+
902
+ // anything starting with # is an error. the only thing we should
903
+ // have is #REF, but maybe that will change in the future.
904
+
905
+ if (identifier[0] === '#') {
906
+ return () => ReferenceError();
907
+ }
908
+
909
+ const upper_case = identifier.toUpperCase();
910
+
911
+ switch (upper_case){
912
+ case 'FALSE':
913
+ case 'F':
914
+ return () => {return {value: false, type: ValueType.boolean}};
915
+
916
+ case 'TRUE':
917
+ case 'T':
918
+ return () => {return {value: true, type: ValueType.boolean}};
919
+
920
+ case 'UNDEFINED':
921
+ return () => {return {value: undefined, type: ValueType.undefined}}; // why do we support this?
922
+ }
923
+
924
+ return () => {
925
+
926
+ /*
927
+ if (this.bound_name_stack[0]) {
928
+ const expr = this.bound_name_stack[0][upper_case];
929
+ if (expr) {
930
+ console.info("BOUND", upper_case, expr);
931
+ return this.CalculateExpression(expr as ExtendedExpressionUnit);
932
+ }
933
+ }
934
+ */
935
+
936
+ const named_range = this.named_range_map[upper_case];
937
+
938
+ if (named_range) {
939
+ if (named_range.count === 1) {
940
+ return this.CellFunction4(named_range.start, named_range.start);
941
+ }
942
+ else {
943
+ return this.CellFunction4(named_range.start, named_range.end);
944
+ }
945
+ }
946
+
947
+ const named_expression = this.data_model.named_expressions.get(upper_case);
948
+ if (named_expression) {
949
+ return this.CalculateExpression(named_expression as ExtendedExpressionUnit);
950
+ }
951
+
952
+ /*
953
+ const bound_names = this.context.name_stack[0];
954
+
955
+ if (bound_names && bound_names[upper_case]) {
956
+ const bound_expression = bound_names[upper_case];
957
+ return this.CalculateExpression(bound_expression);
958
+ }
959
+ */
960
+
961
+ // console.info( '** identifier', {identifier, expr, context: this.context});
962
+
963
+ return NameError();
964
+
965
+ };
966
+
967
+ }
968
+
969
+ protected GroupExpression(x: UnitGroup): (expr: UnitGroup) => UnionValue /*UnionOrArray*/ {
970
+
971
+ // a group is an expression in parentheses, either explicit
972
+ // (from the user) or implicit (created to manage operation
973
+ // priority, order of operations, or similar).
974
+
975
+ // expressions nest, so there's no case where a group should
976
+ // have length !== 1 -- consider that an error.
977
+
978
+ if (!x.elements || x.elements.length !== 1){
979
+ console.warn( `Can't handle group !== 1` );
980
+ return () => ExpressionError();
981
+ }
982
+ return (expr: UnitGroup) => this.CalculateExpression(expr.elements[0] as ExtendedExpressionUnit);
983
+ }
984
+
985
+ protected CalculateExpression(expr: ExtendedExpressionUnit, return_reference = false): UnionValue /*UnionOrArray*/ {
986
+
987
+ // user data is a generated function for the expression, at least
988
+ // for the simple ones (atm). see BinaryExpression for more. the
989
+ // aim is to remove as many tests and lookups as possible.
990
+
991
+ // may be over-optimizing here.
992
+
993
+ if (expr.user_data) {
994
+ return expr.user_data(expr);
995
+ }
996
+
997
+ switch (expr.type){
998
+ case 'call':
999
+ {
1000
+ const macro = this.data_model.macro_functions.get(expr.name.toUpperCase());
1001
+ if (macro) {
1002
+ return (expr.user_data = this.CallMacro(expr, macro))(expr);
1003
+ }
1004
+ return (expr.user_data = this.CallExpression(expr, return_reference))(expr);
1005
+ }
1006
+
1007
+ case 'address':
1008
+ return (expr.user_data = this.CellFunction2(expr))(); // check
1009
+
1010
+ case 'range':
1011
+ return (expr.user_data = (x: UnitRange) => this.CellFunction4(x.start, x.end))(expr); // check
1012
+
1013
+ case 'binary':
1014
+ return (expr.user_data = this.BinaryExpression(expr))(expr); // check
1015
+
1016
+ case 'unary':
1017
+ return (expr.user_data = this.UnaryExpression(expr))(expr); // check
1018
+
1019
+ case 'identifier':
1020
+ return (expr.user_data = this.Identifier(expr))(); // check
1021
+
1022
+ case 'missing':
1023
+ return (expr.user_data = () => { return { value: undefined, type: ValueType.undefined } as UndefinedUnion })(); // check
1024
+
1025
+ case 'dimensioned':
1026
+ return (expr.user_data = this.ResolveDimensionedQuantity())(expr);
1027
+
1028
+ case 'literal':
1029
+ {
1030
+ const literal = { value: expr.value, type: GetValueType(expr.value) } as UnionValue;
1031
+ return (expr.user_data = () => literal)(); // check
1032
+ }
1033
+ case 'group':
1034
+ return (expr.user_data = this.GroupExpression(expr))(expr); // check
1035
+
1036
+ case 'complex':
1037
+ {
1038
+ const literal = {value: {real: expr.real, imaginary: expr.imaginary}, type: ValueType.complex } as ComplexUnion;
1039
+ return (expr.user_data = () => literal)(); // check
1040
+ }
1041
+
1042
+ case 'structured-reference':
1043
+ return (expr.user_data = this.ResolveStructuredReference(expr))();
1044
+
1045
+ case 'array':
1046
+ {
1047
+ return (expr.user_data = () => {
1048
+ return {
1049
+ type: ValueType.array,
1050
+ value: expr.values.map((row: any) => (Array.isArray(row) ? row : [row]).map((value: any) => {
1051
+ return { type: GetValueType(value), value } as UnionValue;
1052
+ })),
1053
+ } as ArrayUnion;
1054
+ })();
1055
+ /*
1056
+ return (expr.user_data = () => expr.values.map(row => (Array.isArray(row) ? row : [row]).map(value => {
1057
+ return { value, type: GetValueType(value) }
1058
+ })))(); // check
1059
+ */
1060
+ }
1061
+
1062
+ default:
1063
+ console.warn( 'Unhandled parse expr:', expr);
1064
+ return UnknownError();
1065
+ }
1066
+ }
1067
+
1068
+ }
1069
+