@borgar/fx 4.13.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 (141) 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.js → advRangeOp.ts} +1 -1
  36. package/lib/lexers/{canEndRange.js → canEndRange.ts} +2 -2
  37. package/lib/lexers/{lexBoolean.js → lexBoolean.ts} +25 -6
  38. package/lib/lexers/{lexContext.js → lexContext.ts} +14 -6
  39. package/lib/lexers/{lexError.js → lexError.ts} +3 -3
  40. package/lib/lexers/{lexFunction.js → lexFunction.ts} +3 -2
  41. package/lib/lexers/lexNameFuncCntx.ts +112 -0
  42. package/lib/lexers/{lexNamed.js → lexNamed.ts} +4 -4
  43. package/lib/lexers/{lexNewLine.js → lexNewLine.ts} +3 -2
  44. package/lib/lexers/{lexNumber.js → lexNumber.ts} +4 -3
  45. package/lib/lexers/{lexOperator.js → lexOperator.ts} +5 -4
  46. package/lib/lexers/lexRange.ts +15 -0
  47. package/lib/lexers/{lexRangeA1.js → lexRangeA1.ts} +11 -7
  48. package/lib/lexers/{lexRangeR1C1.js → lexRangeR1C1.ts} +10 -6
  49. package/lib/lexers/{lexRangeTrim.js → lexRangeTrim.ts} +3 -2
  50. package/lib/lexers/{lexRefOp.js → lexRefOp.ts} +4 -3
  51. package/lib/lexers/{lexString.js → lexString.ts} +3 -3
  52. package/lib/lexers/{lexStructured.js → lexStructured.ts} +5 -5
  53. package/lib/lexers/{lexWhitespace.js → lexWhitespace.ts} +3 -2
  54. package/lib/lexers/sets.ts +51 -0
  55. package/lib/mergeRefTokens.spec.ts +141 -0
  56. package/lib/{mergeRefTokens.js → mergeRefTokens.ts} +14 -9
  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.js → parseSRange.ts} +15 -10
  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 +45 -31
  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/benchmark/benchmark.js +0 -48
  104. package/benchmark/formulas.json +0 -15677
  105. package/dist/fx.d.ts +0 -823
  106. package/dist/fx.js +0 -2
  107. package/dist/package.json +0 -1
  108. package/lib/a1.js +0 -348
  109. package/lib/a1.spec.js +0 -458
  110. package/lib/addTokenMeta.spec.js +0 -153
  111. package/lib/astTypes.js +0 -96
  112. package/lib/extraTypes.js +0 -74
  113. package/lib/fixRanges.js +0 -104
  114. package/lib/fixRanges.spec.js +0 -171
  115. package/lib/fromCol.spec.js +0 -11
  116. package/lib/index.js +0 -134
  117. package/lib/index.spec.js +0 -67
  118. package/lib/isType.spec.js +0 -168
  119. package/lib/lexer-srefs.spec.js +0 -324
  120. package/lib/lexer.js +0 -264
  121. package/lib/lexer.spec.js +0 -1953
  122. package/lib/lexers/lexRange.js +0 -8
  123. package/lib/lexers/sets.js +0 -38
  124. package/lib/mergeRefTokens.spec.js +0 -121
  125. package/lib/package.json +0 -1
  126. package/lib/parseRef.js +0 -157
  127. package/lib/parseRef.spec.js +0 -71
  128. package/lib/parseStructRef.js +0 -48
  129. package/lib/parseStructRef.spec.js +0 -164
  130. package/lib/parser.spec.js +0 -1208
  131. package/lib/rc.js +0 -341
  132. package/lib/rc.spec.js +0 -403
  133. package/lib/stringifyStructRef.js +0 -80
  134. package/lib/stringifyStructRef.spec.js +0 -182
  135. package/lib/toCol.spec.js +0 -11
  136. package/lib/translate-toA1.spec.js +0 -214
  137. package/lib/translate-toRC.spec.js +0 -197
  138. package/lib/translate.js +0 -239
  139. package/lib/translate.spec.js +0 -21
  140. package/rollup.config.mjs +0 -22
  141. 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
+ });