@borgar/fx 4.12.0 → 5.0.0

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 (139) hide show
  1. package/dist/index-BMr6cTgc.d.cts +1444 -0
  2. package/dist/index-BMr6cTgc.d.ts +1444 -0
  3. package/dist/index.cjs +3054 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +2984 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/xlsx/index.cjs +3120 -0
  10. package/dist/xlsx/index.cjs.map +1 -0
  11. package/dist/xlsx/index.d.cts +55 -0
  12. package/dist/xlsx/index.d.ts +55 -0
  13. package/dist/xlsx/index.js +3049 -0
  14. package/dist/xlsx/index.js.map +1 -0
  15. package/docs/API.md +2959 -718
  16. package/docs/AST_format.md +2 -2
  17. package/eslint.config.mjs +40 -0
  18. package/lib/a1.spec.ts +32 -0
  19. package/lib/a1.ts +26 -0
  20. package/lib/addA1RangeBounds.ts +50 -0
  21. package/lib/addTokenMeta.spec.ts +166 -0
  22. package/lib/{addTokenMeta.js → addTokenMeta.ts} +53 -33
  23. package/lib/astTypes.ts +211 -0
  24. package/lib/cloneToken.ts +29 -0
  25. package/lib/{constants.js → constants.ts} +6 -3
  26. package/lib/fixRanges.spec.ts +220 -0
  27. package/lib/fixRanges.ts +260 -0
  28. package/lib/fromCol.spec.ts +15 -0
  29. package/lib/{fromCol.js → fromCol.ts} +1 -1
  30. package/lib/index.spec.ts +119 -0
  31. package/lib/index.ts +76 -0
  32. package/lib/isNodeType.ts +151 -0
  33. package/lib/isType.spec.ts +208 -0
  34. package/lib/{isType.js → isType.ts} +26 -25
  35. package/lib/lexers/advRangeOp.ts +18 -0
  36. package/lib/lexers/canEndRange.ts +25 -0
  37. package/lib/lexers/lexBoolean.ts +55 -0
  38. package/lib/lexers/lexContext.ts +104 -0
  39. package/lib/lexers/lexError.ts +15 -0
  40. package/lib/lexers/lexFunction.ts +37 -0
  41. package/lib/lexers/lexNameFuncCntx.ts +112 -0
  42. package/lib/lexers/lexNamed.ts +60 -0
  43. package/lib/lexers/lexNewLine.ts +12 -0
  44. package/lib/lexers/lexNumber.ts +48 -0
  45. package/lib/lexers/lexOperator.ts +26 -0
  46. package/lib/lexers/lexRange.ts +15 -0
  47. package/lib/lexers/lexRangeA1.ts +134 -0
  48. package/lib/lexers/lexRangeR1C1.ts +146 -0
  49. package/lib/lexers/lexRangeTrim.ts +26 -0
  50. package/lib/lexers/lexRefOp.ts +19 -0
  51. package/lib/lexers/lexString.ts +22 -0
  52. package/lib/lexers/lexStructured.ts +25 -0
  53. package/lib/lexers/lexWhitespace.ts +31 -0
  54. package/lib/lexers/sets.ts +51 -0
  55. package/lib/mergeRefTokens.spec.ts +141 -0
  56. package/lib/{mergeRefTokens.js → mergeRefTokens.ts} +47 -32
  57. package/lib/nodeTypes.ts +54 -0
  58. package/lib/parse.spec.ts +1410 -0
  59. package/lib/{parser.js → parse.ts} +81 -63
  60. package/lib/parseA1Range.spec.ts +233 -0
  61. package/lib/parseA1Range.ts +206 -0
  62. package/lib/parseA1Ref.spec.ts +337 -0
  63. package/lib/parseA1Ref.ts +115 -0
  64. package/lib/parseR1C1Range.ts +191 -0
  65. package/lib/parseR1C1Ref.spec.ts +323 -0
  66. package/lib/parseR1C1Ref.ts +127 -0
  67. package/lib/parseRef.spec.ts +90 -0
  68. package/lib/parseRef.ts +240 -0
  69. package/lib/parseSRange.ts +240 -0
  70. package/lib/parseStructRef.spec.ts +168 -0
  71. package/lib/parseStructRef.ts +76 -0
  72. package/lib/stringifyA1Range.spec.ts +72 -0
  73. package/lib/stringifyA1Range.ts +72 -0
  74. package/lib/stringifyA1Ref.spec.ts +64 -0
  75. package/lib/stringifyA1Ref.ts +59 -0
  76. package/lib/{stringifyPrefix.js → stringifyPrefix.ts} +17 -2
  77. package/lib/stringifyR1C1Range.spec.ts +92 -0
  78. package/lib/stringifyR1C1Range.ts +73 -0
  79. package/lib/stringifyR1C1Ref.spec.ts +63 -0
  80. package/lib/stringifyR1C1Ref.ts +67 -0
  81. package/lib/stringifyStructRef.spec.ts +124 -0
  82. package/lib/stringifyStructRef.ts +113 -0
  83. package/lib/stringifyTokens.ts +15 -0
  84. package/lib/toCol.spec.ts +11 -0
  85. package/lib/{toCol.js → toCol.ts} +4 -4
  86. package/lib/tokenTypes.ts +76 -0
  87. package/lib/tokenize-srefs.spec.ts +429 -0
  88. package/lib/tokenize.spec.ts +2103 -0
  89. package/lib/tokenize.ts +346 -0
  90. package/lib/translate.spec.ts +35 -0
  91. package/lib/translateToA1.spec.ts +247 -0
  92. package/lib/translateToA1.ts +231 -0
  93. package/lib/translateToR1C1.spec.ts +227 -0
  94. package/lib/translateToR1C1.ts +145 -0
  95. package/lib/types.ts +179 -0
  96. package/lib/xlsx/index.spec.ts +27 -0
  97. package/lib/xlsx/index.ts +32 -0
  98. package/package.json +46 -30
  99. package/tsconfig.json +28 -0
  100. package/typedoc-ignore-links.ts +17 -0
  101. package/typedoc.json +41 -0
  102. package/.eslintrc +0 -22
  103. package/dist/fx.d.ts +0 -823
  104. package/dist/fx.js +0 -2
  105. package/dist/package.json +0 -1
  106. package/lib/a1.js +0 -348
  107. package/lib/a1.spec.js +0 -458
  108. package/lib/addTokenMeta.spec.js +0 -153
  109. package/lib/astTypes.js +0 -96
  110. package/lib/extraTypes.js +0 -74
  111. package/lib/fixRanges.js +0 -104
  112. package/lib/fixRanges.spec.js +0 -170
  113. package/lib/fromCol.spec.js +0 -11
  114. package/lib/index.js +0 -134
  115. package/lib/index.spec.js +0 -67
  116. package/lib/isType.spec.js +0 -168
  117. package/lib/lexer-srefs.spec.js +0 -324
  118. package/lib/lexer.js +0 -283
  119. package/lib/lexer.spec.js +0 -1953
  120. package/lib/lexerParts.js +0 -228
  121. package/lib/mergeRefTokens.spec.js +0 -121
  122. package/lib/package.json +0 -1
  123. package/lib/parseRef.js +0 -157
  124. package/lib/parseRef.spec.js +0 -71
  125. package/lib/parseSRange.js +0 -167
  126. package/lib/parseStructRef.js +0 -48
  127. package/lib/parseStructRef.spec.js +0 -164
  128. package/lib/parser.spec.js +0 -1208
  129. package/lib/rc.js +0 -341
  130. package/lib/rc.spec.js +0 -403
  131. package/lib/stringifyStructRef.js +0 -80
  132. package/lib/stringifyStructRef.spec.js +0 -182
  133. package/lib/toCol.spec.js +0 -11
  134. package/lib/translate-toA1.spec.js +0 -214
  135. package/lib/translate-toRC.spec.js +0 -197
  136. package/lib/translate.js +0 -239
  137. package/lib/translate.spec.js +0 -21
  138. package/rollup.config.mjs +0 -22
  139. package/tsd.json +0 -12
@@ -0,0 +1,429 @@
1
+ import { describe, test, expect } from 'vitest';
2
+ import {
3
+ FX_PREFIX, UNKNOWN,
4
+ OPERATOR, WHITESPACE,
5
+ REF_NAMED, CONTEXT_QUOTE, REF_STRUCT, REF_RANGE
6
+ } from './constants.ts';
7
+ import { tokenize } from './tokenize.ts';
8
+
9
+ function isTokens (expr: string, result: any[], opts?: any) {
10
+ expect(tokenize(expr, { negativeNumbers: false, ...opts })).toEqual(result);
11
+ }
12
+
13
+ describe('tokenize structured references', () => {
14
+ describe('basic structured references (default merge on)', () => {
15
+ test('keyword specifiers', () => {
16
+ isTokens('[@]', [
17
+ { type: REF_STRUCT, value: '[@]' }
18
+ ]);
19
+
20
+ isTokens('[[@]]', [
21
+ { type: UNKNOWN, value: '[' },
22
+ { type: REF_STRUCT, value: '[@]' },
23
+ { type: UNKNOWN, value: ']' }
24
+ ]);
25
+
26
+ isTokens('[@foo]', [
27
+ { type: REF_STRUCT, value: '[@foo]' }
28
+ ]);
29
+
30
+ isTokens('[Column Name]', [
31
+ { type: REF_STRUCT, value: '[Column Name]' }
32
+ ]);
33
+ });
34
+
35
+ test('range specifiers with columns', () => {
36
+ isTokens('[@foo:bar]', [
37
+ { type: REF_STRUCT, value: '[@foo:bar]' }
38
+ ]);
39
+
40
+ isTokens('[@[foo]:bar]', [
41
+ { type: REF_STRUCT, value: '[@[foo]:bar]' }
42
+ ]);
43
+
44
+ isTokens('[@[foo]:[bar]]', [
45
+ { type: REF_STRUCT, value: '[@[foo]:[bar]]' }
46
+ ]);
47
+
48
+ isTokens('[@foo:[bar]]', [
49
+ { type: REF_STRUCT, value: '[@foo:[bar]]' }
50
+ ]);
51
+
52
+ isTokens('[@[foo]]', [
53
+ { type: REF_STRUCT, value: '[@[foo]]' }
54
+ ]);
55
+ });
56
+
57
+ test('nested bracket handling', () => {
58
+ isTokens('[[@foo]]', [
59
+ { type: UNKNOWN, value: '[' },
60
+ { type: REF_STRUCT, value: '[@foo]' },
61
+ { type: UNKNOWN, value: ']' }
62
+ ]);
63
+
64
+ isTokens('[[\'@foo]]', [
65
+ { type: REF_STRUCT, value: '[[\'@foo]]' }
66
+ ]);
67
+ });
68
+
69
+ test('special identifiers', () => {
70
+ isTokens('[#All]', [
71
+ { type: REF_STRUCT, value: '[#All]' }
72
+ ]);
73
+
74
+ isTokens('[#Data]', [
75
+ { type: REF_STRUCT, value: '[#Data]' }
76
+ ]);
77
+
78
+ isTokens('[#Headers]', [
79
+ { type: REF_STRUCT, value: '[#Headers]' }
80
+ ]);
81
+
82
+ isTokens('[#Totals]', [
83
+ { type: REF_STRUCT, value: '[#Totals]' }
84
+ ]);
85
+
86
+ isTokens('[#This Row]', [
87
+ { type: REF_STRUCT, value: '[#This Row]' }
88
+ ]);
89
+ });
90
+
91
+ test('case sensitivity for special identifiers', () => {
92
+ isTokens('[#totals]', [
93
+ { type: REF_STRUCT, value: '[#totals]' }
94
+ ]);
95
+
96
+ isTokens('[#tOtAlS]', [
97
+ { type: REF_STRUCT, value: '[#tOtAlS]' }
98
+ ]);
99
+ });
100
+
101
+ test('invalid special identifier formats', () => {
102
+ isTokens('[#This Row]', [
103
+ { type: UNKNOWN, value: '[' },
104
+ { type: OPERATOR, value: '#' },
105
+ { type: REF_NAMED, value: 'This' },
106
+ { type: WHITESPACE, value: ' ' },
107
+ { type: UNKNOWN, value: 'Row]' }
108
+ ]);
109
+
110
+ isTokens('[ #tOtAlS]', [
111
+ { type: UNKNOWN, value: '[' },
112
+ { type: WHITESPACE, value: ' ' },
113
+ { type: OPERATOR, value: '#' },
114
+ { type: UNKNOWN, value: 'tOtAlS]' }
115
+ ]);
116
+
117
+ isTokens('[#tOtAlS ]', [
118
+ { type: UNKNOWN, value: '[' },
119
+ { type: OPERATOR, value: '#' },
120
+ { type: REF_NAMED, value: 'tOtAlS' },
121
+ { type: WHITESPACE, value: ' ' },
122
+ { type: UNKNOWN, value: ']' }
123
+ ]);
124
+
125
+ isTokens('[# tOtAlS ]', [
126
+ { type: UNKNOWN, value: '[' },
127
+ { type: OPERATOR, value: '#' },
128
+ { type: WHITESPACE, value: ' ' },
129
+ { type: REF_NAMED, value: 'tOtAlS' },
130
+ { type: WHITESPACE, value: ' ' },
131
+ { type: UNKNOWN, value: ']' }
132
+ ]);
133
+ });
134
+
135
+ test('complex nested references', () => {
136
+ isTokens('[[#all],@[foo]]', [
137
+ { type: UNKNOWN, value: '[' },
138
+ { type: REF_STRUCT, value: '[#all]' },
139
+ { type: OPERATOR, value: ',' },
140
+ { type: OPERATOR, value: '@' },
141
+ { type: REF_STRUCT, value: '[foo]' },
142
+ { type: UNKNOWN, value: ']' }
143
+ ]);
144
+
145
+ isTokens('[[#all],]', [
146
+ { type: UNKNOWN, value: '[' },
147
+ { type: REF_STRUCT, value: '[#all]' },
148
+ { type: OPERATOR, value: ',' },
149
+ { type: UNKNOWN, value: ']' }
150
+ ]);
151
+
152
+ isTokens('[[#data][#headers]]', [
153
+ { type: UNKNOWN, value: '[' },
154
+ { type: REF_STRUCT, value: '[#data]' },
155
+ { type: REF_STRUCT, value: '[#headers]' },
156
+ { type: UNKNOWN, value: ']' }
157
+ ]);
158
+
159
+ isTokens('[[#data]foo]', [
160
+ { type: UNKNOWN, value: '[' },
161
+ { type: REF_STRUCT, value: '[#data]' },
162
+ { type: UNKNOWN, value: 'foo]' }
163
+ ]);
164
+ });
165
+
166
+ test('valid complex structured references', () => {
167
+ isTokens('[[#all],[foo]]', [
168
+ { type: REF_STRUCT, value: '[[#all],[foo]]' }
169
+ ]);
170
+
171
+ isTokens('[[#all],foo]', [
172
+ { type: REF_STRUCT, value: '[[#all],foo]' }
173
+ ]);
174
+
175
+ isTokens('[[#all],foo:bar]', [
176
+ { type: REF_STRUCT, value: '[[#all],foo:bar]' }
177
+ ]);
178
+
179
+ isTokens('[[#all],[foo]:[bar]]', [
180
+ { type: REF_STRUCT, value: '[[#all],[foo]:[bar]]' }
181
+ ]);
182
+ });
183
+
184
+ test('column names with special characters', () => {
185
+ // this may not be what users expect, but "foo:bar baz" is a legit column name
186
+ isTokens('[foo:bar baz]', [
187
+ { type: REF_STRUCT, value: '[foo:bar baz]' }
188
+ ]);
189
+
190
+ isTokens('[foo:[bar baz]]', [
191
+ { type: REF_STRUCT, value: '[foo:[bar baz]]' }
192
+ ]);
193
+
194
+ isTokens('[foo:]', [
195
+ { type: REF_STRUCT, value: '[foo:]' }
196
+ ]);
197
+
198
+ isTokens('[[foo]:[bar baz]]', [
199
+ { type: REF_STRUCT, value: '[[foo]:[bar baz]]' }
200
+ ]);
201
+ });
202
+
203
+ test('complex multi-section references', () => {
204
+ isTokens('[[#headers],[#data],[#headers],[#data],[#headers],[#data],[Baz]]', [
205
+ { type: REF_STRUCT, value: '[[#headers],[#data],[#headers],[#data],[#headers],[#data],[Baz]]' }
206
+ ]);
207
+
208
+ isTokens('[[#all],[#all],[#all],[#all],[ColumnName]]', [
209
+ { type: REF_STRUCT, value: '[[#all],[#all],[#all],[#all],[ColumnName]]' }
210
+ ]);
211
+
212
+ isTokens('[[#Totals],col name:Foo]', [
213
+ { type: REF_STRUCT, value: '[[#Totals],col name:Foo]' }
214
+ ]);
215
+ });
216
+
217
+ test('mixed with other token types', () => {
218
+ isTokens('Table1[[#This Row],[a]]*[1]Sheet1!$A$1', [
219
+ { type: REF_STRUCT, value: 'Table1[[#This Row],[a]]' },
220
+ { type: OPERATOR, value: '*' },
221
+ { type: REF_RANGE, value: '[1]Sheet1!$A$1' }
222
+ ], { xlsx: true });
223
+
224
+ isTokens("Sheet1!Table1[foo '[bar']]", [
225
+ { type: REF_STRUCT, value: "Sheet1!Table1[foo '[bar']]" }
226
+ ]);
227
+ });
228
+ });
229
+
230
+ describe('with mergeRefs disabled', () => {
231
+ const mergeOffOpts = { mergeRefs: false };
232
+
233
+ test('table references separated from structured parts', () => {
234
+ isTokens('Table1[[#This Row],[Column]]', [
235
+ { type: REF_NAMED, value: 'Table1' },
236
+ { type: REF_STRUCT, value: '[[#This Row],[Column]]' }
237
+ ], mergeOffOpts);
238
+
239
+ isTokens('DeptSales[[#Headers],[#Data],[% Commission]]', [
240
+ { type: REF_NAMED, value: 'DeptSales' },
241
+ { type: REF_STRUCT, value: '[[#Headers],[#Data],[% Commission]]' }
242
+ ], mergeOffOpts);
243
+
244
+ isTokens('Table1[[#This Row],[Column Name]]', [
245
+ { type: REF_NAMED, value: 'Table1' },
246
+ { type: REF_STRUCT, value: '[[#This Row],[Column Name]]' }
247
+ ], mergeOffOpts);
248
+ });
249
+
250
+ test('column references', () => {
251
+ isTokens('Table1[@[Column]]', [
252
+ { type: REF_NAMED, value: 'Table1' },
253
+ { type: REF_STRUCT, value: '[@[Column]]' }
254
+ ], mergeOffOpts);
255
+
256
+ isTokens('Table1[@Column]', [
257
+ { type: REF_NAMED, value: 'Table1' },
258
+ { type: REF_STRUCT, value: '[@Column]' }
259
+ ], mergeOffOpts);
260
+
261
+ isTokens('Table1[ [#Data], [Surf]:[Rod] ]', [
262
+ { type: REF_NAMED, value: 'Table1' },
263
+ { type: REF_STRUCT, value: '[ [#Data], [Surf]:[Rod] ]' }
264
+ ], mergeOffOpts);
265
+
266
+ // Excel does pick this up but normalizes to DeptSales[@[Commission Amount]]
267
+ isTokens('DeptSales[@Commission Amount]', [
268
+ { type: REF_NAMED, value: 'DeptSales' },
269
+ { type: REF_STRUCT, value: '[@Commission Amount]' }
270
+ ], mergeOffOpts);
271
+ });
272
+
273
+ test('complex table references', () => {
274
+ isTokens('DeptSales[[#Totals],[Sales Amount]:[Commission Amount]]', [
275
+ { type: REF_NAMED, value: 'DeptSales' },
276
+ { type: REF_STRUCT, value: '[[#Totals],[Sales Amount]:[Commission Amount]]' }
277
+ ], mergeOffOpts);
278
+
279
+ isTokens('DeptSales[[#Headers],[Region]:[Commission Amount]]', [
280
+ { type: REF_NAMED, value: 'DeptSales' },
281
+ { type: REF_STRUCT, value: '[[#Headers],[Region]:[Commission Amount]]' }
282
+ ], mergeOffOpts);
283
+
284
+ isTokens('DeptSales[\'#OfItems]', [
285
+ { type: REF_NAMED, value: 'DeptSales' },
286
+ { type: REF_STRUCT, value: '[\'#OfItems]' }
287
+ ], mergeOffOpts);
288
+
289
+ isTokens('Table1[[#Data],[#Totals],Bar:Baz]', [
290
+ { type: REF_NAMED, value: 'Table1' },
291
+ { type: REF_STRUCT, value: '[[#Data],[#Totals],Bar:Baz]' }
292
+ ], mergeOffOpts);
293
+ });
294
+
295
+ test('range operations between table references', () => {
296
+ isTokens('Table1[[Foo]:[Bar]]:Table1[Baz]', [
297
+ { type: REF_NAMED, value: 'Table1' },
298
+ { type: REF_STRUCT, value: '[[Foo]:[Bar]]' },
299
+ { type: OPERATOR, value: ':' },
300
+ { type: REF_NAMED, value: 'Table1' },
301
+ { type: REF_STRUCT, value: '[Baz]' }
302
+ ], mergeOffOpts);
303
+
304
+ isTokens('Table1[Bar]:Table1[Baz]', [
305
+ { type: REF_NAMED, value: 'Table1' },
306
+ { type: REF_STRUCT, value: '[Bar]' },
307
+ { type: OPERATOR, value: ':' },
308
+ { type: REF_NAMED, value: 'Table1' },
309
+ { type: REF_STRUCT, value: '[Baz]' }
310
+ ], mergeOffOpts);
311
+ });
312
+
313
+ test('special characters in column names', () => {
314
+ isTokens('Table1[[#Headers],[My\'#thing]]', [
315
+ { type: REF_NAMED, value: 'Table1' },
316
+ { type: REF_STRUCT, value: '[[#Headers],[My\'#thing]]' }
317
+ ], mergeOffOpts);
318
+
319
+ isTokens('DeptSales[Sales Amount]*DeptSales[% Commission]', [
320
+ { type: REF_NAMED, value: 'DeptSales' },
321
+ { type: REF_STRUCT, value: '[Sales Amount]' },
322
+ { type: OPERATOR, value: '*' },
323
+ { type: REF_NAMED, value: 'DeptSales' },
324
+ { type: REF_STRUCT, value: '[% Commission]' }
325
+ ], mergeOffOpts);
326
+ });
327
+
328
+ test('external workbook references', () => {
329
+ isTokens('=\'Sales - May2020.xlsx\'!Table1[ [#Data], [#Totals], [Surf]:[Rod] ]', [
330
+ { type: FX_PREFIX, value: '=' },
331
+ { type: CONTEXT_QUOTE, value: '\'Sales - May2020.xlsx\'' },
332
+ { type: OPERATOR, value: '!' },
333
+ { type: REF_NAMED, value: 'Table1' },
334
+ { type: REF_STRUCT, value: '[ [#Data], [#Totals], [Surf]:[Rod] ]' }
335
+ ], mergeOffOpts);
336
+ });
337
+ });
338
+
339
+ describe('with mergeRefs enabled (default)', () => {
340
+ test('table references merged with structured parts', () => {
341
+ isTokens('Table1[[#This Row],[Column]]', [
342
+ { type: REF_STRUCT, value: 'Table1[[#This Row],[Column]]' }
343
+ ]);
344
+
345
+ isTokens('DeptSales[[#Headers],[#Data],[% Commission]]', [
346
+ { type: REF_STRUCT, value: 'DeptSales[[#Headers],[#Data],[% Commission]]' }
347
+ ]);
348
+
349
+ isTokens('Table1[[#This Row],[Column Name]]', [
350
+ { type: REF_STRUCT, value: 'Table1[[#This Row],[Column Name]]' }
351
+ ]);
352
+ });
353
+
354
+ test('column references merged', () => {
355
+ isTokens('Table1[@[Column]]', [
356
+ { type: REF_STRUCT, value: 'Table1[@[Column]]' }
357
+ ]);
358
+
359
+ isTokens('Table1[@Column]', [
360
+ { type: REF_STRUCT, value: 'Table1[@Column]' }
361
+ ]);
362
+
363
+ isTokens('Table1[ [#Data], [Surf]:[Rod] ]', [
364
+ { type: REF_STRUCT, value: 'Table1[ [#Data], [Surf]:[Rod] ]' }
365
+ ]);
366
+
367
+ // Excel does pick this up but normalizes to DeptSales[@[Commission Amount]]
368
+ isTokens('DeptSales[@Commission Amount]', [
369
+ { type: REF_STRUCT, value: 'DeptSales[@Commission Amount]' }
370
+ ]);
371
+ });
372
+
373
+ test('complex table references merged', () => {
374
+ isTokens('DeptSales[[#Totals],[Sales Amount]:[Commission Amount]]', [
375
+ { type: REF_STRUCT, value: 'DeptSales[[#Totals],[Sales Amount]:[Commission Amount]]' }
376
+ ]);
377
+
378
+ isTokens('DeptSales[[#Headers],[Region]:[Commission Amount]]', [
379
+ { type: REF_STRUCT, value: 'DeptSales[[#Headers],[Region]:[Commission Amount]]' }
380
+ ]);
381
+
382
+ isTokens('DeptSales[\'#OfItems]', [
383
+ { type: REF_STRUCT, value: 'DeptSales[\'#OfItems]' }
384
+ ]);
385
+
386
+ isTokens('Table1[[#Data],[#Totals],Bar:Baz]', [
387
+ { type: REF_STRUCT, value: 'Table1[[#Data],[#Totals],Bar:Baz]' }
388
+ ]);
389
+ });
390
+
391
+ test('range operations between merged table references', () => {
392
+ isTokens('Table1[[Foo]:[Bar]]:Table1[Baz]', [
393
+ { type: REF_STRUCT, value: 'Table1[[Foo]:[Bar]]' },
394
+ { type: OPERATOR, value: ':' },
395
+ { type: REF_STRUCT, value: 'Table1[Baz]' }
396
+ ]);
397
+
398
+ isTokens('Table1[Bar]:Table1[Baz]', [
399
+ { type: REF_STRUCT, value: 'Table1[Bar]' },
400
+ { type: OPERATOR, value: ':' },
401
+ { type: REF_STRUCT, value: 'Table1[Baz]' }
402
+ ]);
403
+ });
404
+
405
+ test('special characters in column names merged', () => {
406
+ isTokens('Table1[[#Headers],[My\'#thing]]', [
407
+ { type: REF_STRUCT, value: 'Table1[[#Headers],[My\'#thing]]' }
408
+ ]);
409
+
410
+ isTokens('DeptSales[Sales Amount]*DeptSales[% Commission]', [
411
+ { type: REF_STRUCT, value: 'DeptSales[Sales Amount]' },
412
+ { type: OPERATOR, value: '*' },
413
+ { type: REF_STRUCT, value: 'DeptSales[% Commission]' }
414
+ ]);
415
+ });
416
+
417
+ test('external workbook references merged', () => {
418
+ isTokens('=\'Sales - May2020.xlsx\'!Table1[ [#Data], [#Totals], [Surf]:[Rod] ]', [
419
+ { type: FX_PREFIX, value: '=' },
420
+ { type: REF_STRUCT, value: '\'Sales - May2020.xlsx\'!Table1[ [#Data], [#Totals], [Surf]:[Rod] ]' }
421
+ ]);
422
+
423
+ isTokens('=[myworkbook.xlsx]Sheet1!TMP8w0habhr[#All]', [
424
+ { type: FX_PREFIX, value: '=' },
425
+ { type: REF_STRUCT, value: '[myworkbook.xlsx]Sheet1!TMP8w0habhr[#All]' }
426
+ ]);
427
+ });
428
+ });
429
+ });