@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
@@ -1,1208 +0,0 @@
1
- /* eslint-disable object-property-newline, object-curly-newline */
2
- import { test, Test } from 'tape';
3
- import { parse } from './parser.js';
4
-
5
- Test.prototype.isParsed = function isParsed (expr, expect, opts) {
6
- const result = parse(expr, { allowTernary: true, withLocation: false, ...opts });
7
- const cleaned = JSON.parse(JSON.stringify(result));
8
- this.deepEqual(cleaned, expect, `\x1b[32m${expr}\x1b[0m`);
9
- };
10
-
11
- Test.prototype.isInvalidExpr = function isInvalidExpr (expr, opts) {
12
- this.throws(
13
- () => parse(expr, { allowTernary: true, ...opts }),
14
- `\x1b[36m${expr}\x1b[0m`
15
- );
16
- };
17
-
18
- test('parse numbers', t => {
19
- t.isParsed('1', { type: 'Literal', value: 1, raw: '1' });
20
- t.isParsed('-1', { type: 'Literal', value: -1, raw: '-1' });
21
- t.isParsed('2.4e+3', { type: 'Literal', value: 2400, raw: '2.4e+3' });
22
- t.isParsed('-1e+10', { type: 'Literal', value: -10000000000, raw: '-1e+10' });
23
- t.isParsed('1e-3', { type: 'Literal', value: 0.001, raw: '1e-3' });
24
- t.end();
25
- });
26
-
27
- test('parse booleans', t => {
28
- t.isParsed('TRUE', { type: 'Literal', value: true, raw: 'TRUE' });
29
- t.isParsed('true', { type: 'Literal', value: true, raw: 'true' });
30
- t.isParsed('trUe', { type: 'Literal', value: true, raw: 'trUe' });
31
- t.isParsed('TRue', { type: 'Literal', value: true, raw: 'TRue' });
32
- t.isParsed('FALSE', { type: 'Literal', value: false, raw: 'FALSE' });
33
- t.isParsed('false', { type: 'Literal', value: false, raw: 'false' });
34
- t.isParsed('False', { type: 'Literal', value: false, raw: 'False' });
35
- t.isParsed('fAlSe', { type: 'Literal', value: false, raw: 'fAlSe' });
36
- t.end();
37
- });
38
-
39
- test('parse strings', t => {
40
- t.isParsed('""', { type: 'Literal', value: '', raw: '""' });
41
- t.isParsed('""""', { type: 'Literal', value: '"', raw: '""""' });
42
- t.isParsed('" "', { type: 'Literal', value: ' ', raw: '" "' });
43
- t.isParsed('"foobar"', { type: 'Literal', value: 'foobar', raw: '"foobar"' });
44
- t.isParsed('"foo bar"', { type: 'Literal', value: 'foo bar', raw: '"foo bar"' });
45
- t.isParsed('"foo""bar"', { type: 'Literal', value: 'foo"bar', raw: '"foo""bar"' });
46
- t.end();
47
- });
48
-
49
- test('parse errors', t => {
50
- t.isParsed('#CALC!', { type: 'ErrorLiteral', value: '#CALC!', raw: '#CALC!' });
51
- t.isParsed('#DIV/0!', { type: 'ErrorLiteral', value: '#DIV/0!', raw: '#DIV/0!' });
52
- t.isParsed('#FIELD!', { type: 'ErrorLiteral', value: '#FIELD!', raw: '#FIELD!' });
53
- t.isParsed('#GETTING_DATA', { type: 'ErrorLiteral', value: '#GETTING_DATA', raw: '#GETTING_DATA' });
54
- t.isParsed('#N/A', { type: 'ErrorLiteral', value: '#N/A', raw: '#N/A' });
55
- t.isParsed('#NAME?', { type: 'ErrorLiteral', value: '#NAME?', raw: '#NAME?' });
56
- t.isParsed('#NULL!', { type: 'ErrorLiteral', value: '#NULL!', raw: '#NULL!' });
57
- t.isParsed('#NUM!', { type: 'ErrorLiteral', value: '#NUM!', raw: '#NUM!' });
58
- t.isParsed('#REF!', { type: 'ErrorLiteral', value: '#REF!', raw: '#REF!' });
59
- t.isParsed('#SPILL!', { type: 'ErrorLiteral', value: '#SPILL!', raw: '#SPILL!' });
60
- t.isParsed('#SYNTAX?', { type: 'ErrorLiteral', value: '#SYNTAX?', raw: '#SYNTAX?' });
61
- t.isParsed('#UNKNOWN!', { type: 'ErrorLiteral', value: '#UNKNOWN!', raw: '#UNKNOWN!' });
62
- t.isParsed('#VALUE!', { type: 'ErrorLiteral', value: '#VALUE!', raw: '#VALUE!' });
63
- t.end();
64
- });
65
-
66
- test('parse ranges', t => {
67
- t.isParsed('A1', { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' });
68
- t.isParsed('A1:B2', { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' });
69
- t.isParsed('A:B', { type: 'ReferenceIdentifier', value: 'A:B', kind: 'beam' });
70
- t.isParsed('1:2', { type: 'ReferenceIdentifier', value: '1:2', kind: 'beam' });
71
- t.isParsed('A1:2', { type: 'ReferenceIdentifier', value: 'A1:2', kind: 'range' });
72
- t.isParsed('1:A2', { type: 'ReferenceIdentifier', value: '1:A2', kind: 'range' });
73
- t.isParsed('A1.:.B2', { type: 'ReferenceIdentifier', value: 'A1.:.B2', kind: 'range' });
74
- t.isParsed('Sheet!A1', { type: 'ReferenceIdentifier', value: 'Sheet!A1', kind: 'range' });
75
- t.isParsed('[Workbook]Sheet!A1', { type: 'ReferenceIdentifier', value: '[Workbook]Sheet!A1', kind: 'range' });
76
- t.isParsed('\'Sheet\'!A1', { type: 'ReferenceIdentifier', value: '\'Sheet\'!A1', kind: 'range' });
77
- t.isParsed('\'[Workbook]Sheet\'!A1', { type: 'ReferenceIdentifier', value: '\'[Workbook]Sheet\'!A1', kind: 'range' });
78
- t.isParsed('foo', { type: 'ReferenceIdentifier', value: 'foo', kind: 'name' });
79
- t.isParsed('Workbook!foo', { type: 'ReferenceIdentifier', value: 'Workbook!foo', kind: 'name' });
80
- t.isParsed('[Workbook]Sheet!foo', { type: 'ReferenceIdentifier', value: '[Workbook]Sheet!foo', kind: 'name' });
81
- t.isParsed('\'Workbook\'!A1', { type: 'ReferenceIdentifier', value: '\'Workbook\'!A1', kind: 'range' });
82
- t.isParsed('\'[Workbook]Sheet\'!A1', { type: 'ReferenceIdentifier', value: '\'[Workbook]Sheet\'!A1', kind: 'range' });
83
- t.end();
84
- });
85
-
86
- test('parse array literals', t => {
87
- t.isParsed('{1}', {
88
- type: 'ArrayExpression',
89
- elements: [ [
90
- { type: 'Literal', value: 1, raw: '1' }
91
- ] ]
92
- });
93
- t.isParsed('{-1}', {
94
- type: 'ArrayExpression',
95
- elements: [ [
96
- { type: 'Literal', value: -1, raw: '-1' }
97
- ] ]
98
- });
99
- t.isParsed('{#DIV/0!}', {
100
- type: 'ArrayExpression',
101
- elements: [ [
102
- { type: 'ErrorLiteral', value: '#DIV/0!', raw: '#DIV/0!' }
103
- ] ]
104
- });
105
- t.isParsed('{TRUE}', {
106
- type: 'ArrayExpression',
107
- elements: [ [
108
- { type: 'Literal', value: true, raw: 'TRUE' }
109
- ] ]
110
- });
111
- t.isParsed('{"foo"}', {
112
- type: 'ArrayExpression',
113
- elements: [ [
114
- { type: 'Literal', value: 'foo', raw: '"foo"' }
115
- ] ]
116
- });
117
- t.isParsed('{1,2}', {
118
- type: 'ArrayExpression',
119
- elements: [ [
120
- { type: 'Literal', value: 1, raw: '1' },
121
- { type: 'Literal', value: 2, raw: '2' }
122
- ] ]
123
- });
124
- t.isParsed('{1,2;3}', {
125
- type: 'ArrayExpression',
126
- elements: [ [
127
- { type: 'Literal', value: 1, raw: '1' },
128
- { type: 'Literal', value: 2, raw: '2' }
129
- ], [
130
- { type: 'Literal', value: 3, raw: '3' }
131
- ] ]
132
- });
133
- t.isParsed('{1,2;3,4}', {
134
- type: 'ArrayExpression',
135
- elements: [ [
136
- { type: 'Literal', value: 1, raw: '1' },
137
- { type: 'Literal', value: 2, raw: '2' }
138
- ], [
139
- { type: 'Literal', value: 3, raw: '3' },
140
- { type: 'Literal', value: 4, raw: '4' }
141
- ] ]
142
- });
143
- t.isParsed('{1;2}', {
144
- type: 'ArrayExpression',
145
- elements: [ [
146
- { type: 'Literal', value: 1, raw: '1' }
147
- ], [
148
- { type: 'Literal', value: 2, raw: '2' }
149
- ] ]
150
- });
151
- t.isParsed('{1;2,3}', {
152
- type: 'ArrayExpression',
153
- elements: [ [
154
- { type: 'Literal', value: 1, raw: '1' }
155
- ], [
156
- { type: 'Literal', value: 2, raw: '2' },
157
- { type: 'Literal', value: 3, raw: '3' }
158
- ] ]
159
- });
160
- t.isParsed('{1;2,3}', {
161
- type: 'ArrayExpression',
162
- elements: [ [
163
- { type: 'Literal', value: 1, raw: '1' }
164
- ], [
165
- { type: 'Literal', value: 2, raw: '2' },
166
- { type: 'Literal', value: 3, raw: '3' }
167
- ] ]
168
- });
169
- t.isParsed('{A1,A1:B2;A:A}', {
170
- type: 'ArrayExpression',
171
- elements: [ [
172
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
173
- { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' }
174
- ], [
175
- { type: 'ReferenceIdentifier', value: 'A:A', kind: 'beam' }
176
- ] ]
177
- }, { permitArrayRanges: true });
178
- t.isParsed('{-0.1,"foo";#NAME?,false}', {
179
- type: 'ArrayExpression',
180
- elements: [ [
181
- { type: 'Literal', value: -0.1, raw: '-0.1' },
182
- { type: 'Literal', value: 'foo', raw: '"foo"' }
183
- ], [
184
- { type: 'ErrorLiteral', value: '#NAME?', raw: '#NAME?' },
185
- { type: 'Literal', value: false, raw: 'false' }
186
- ] ]
187
- });
188
- t.isParsed('{-0.1,"foo";#NAME?,false}', {
189
- type: 'ArrayExpression',
190
- elements: [ [
191
- { type: 'Literal', value: -0.1, raw: '-0.1' },
192
- { type: 'Literal', value: 'foo', raw: '"foo"' }
193
- ], [
194
- { type: 'ErrorLiteral', value: '#NAME?', raw: '#NAME?' },
195
- { type: 'Literal', value: false, raw: 'false' }
196
- ] ]
197
- });
198
- // TODO: consider supporting this?:
199
- // t.isParsed('{-0.1}', {
200
- // type: 'ArrayExpression',
201
- // elements: [ [
202
- // { type: 'Literal', value: -0.1, raw: '-0.1' }
203
- // ] ]
204
- // }, { negativeNumbers: false });
205
- t.isInvalidExpr('{A1}', { permitArrayRanges: false });
206
- t.isInvalidExpr('{--1}', { negativeNumbers: true });
207
- t.isInvalidExpr('{--1}', { negativeNumbers: false });
208
- t.isInvalidExpr('{---1}', { negativeNumbers: true });
209
- t.isInvalidExpr('{---1}', { negativeNumbers: false });
210
- t.isInvalidExpr('{+1}'); // Excel silently corrects this 🤔
211
- t.isInvalidExpr('{(1)}');
212
- t.isInvalidExpr('{SUM(1)}');
213
- t.isInvalidExpr('{{}}');
214
- t.isInvalidExpr('{{}');
215
- t.isInvalidExpr('{}}');
216
- t.isInvalidExpr('{2+2}');
217
- t.isInvalidExpr('{}');
218
- t.isInvalidExpr('{,}');
219
- t.isInvalidExpr('{1,}');
220
- t.isInvalidExpr('{,1}');
221
- t.isInvalidExpr('{;}');
222
-
223
- // permitArrayCalls
224
- t.isParsed('={1234; UNIQUE(A:A)}',
225
- { type: 'ArrayExpression', elements: [
226
- [ { type: 'Literal', value: 1234, raw: '1234' } ],
227
- [ { type: 'CallExpression', callee: { type: 'Identifier', name: 'UNIQUE' }, arguments: [
228
- { type: 'ReferenceIdentifier', value: 'A:A', kind: 'beam' }
229
- ] } ]
230
- ] },
231
- { permitArrayCalls: true });
232
- // permitArrayCalls can be nested
233
- t.isParsed('={SUM({1,2}),3}',
234
- { type: 'ArrayExpression', elements: [
235
- [ { type: 'CallExpression', callee: { type: 'Identifier', name: 'SUM' }, arguments: [
236
- { type: 'ArrayExpression', elements: [ [
237
- { type: 'Literal', value: 1, raw: '1' },
238
- { type: 'Literal', value: 2, raw: '2' }
239
- ] ] }
240
- ] },
241
- { type: 'Literal', value: 3, raw: '3' } ]
242
- ] },
243
- { permitArrayCalls: true });
244
- t.end();
245
- });
246
-
247
- test('parse function calls', t => {
248
- t.isParsed('=foo()', {
249
- type: 'CallExpression', callee: { type: 'Identifier', name: 'foo' },
250
- arguments: []
251
- });
252
- t.isParsed('=FOO()', {
253
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
254
- arguments: []
255
- });
256
- t.isParsed('=FOO(1)', {
257
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
258
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
259
- });
260
- t.isParsed('=FOO(1,2)', {
261
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
262
- arguments: [
263
- { type: 'Literal', value: 1, raw: '1' },
264
- { type: 'Literal', value: 2, raw: '2' }
265
- ]
266
- });
267
- const args = Array(300).fill('1');
268
- t.isParsed(`=FOO(${args.join(',')})`, {
269
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
270
- arguments: [ ...args.map(() => ({ type: 'Literal', value: 1, raw: '1' })) ]
271
- });
272
- t.isParsed('=FOO(A1,B2)', {
273
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
274
- arguments: [
275
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
276
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
277
- ]
278
- });
279
- t.isParsed('=FOO((A1,B2))', {
280
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
281
- arguments: [
282
- { type: 'BinaryExpression', operator: ',', arguments: [
283
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
284
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
285
- ] }
286
- ]
287
- });
288
- t.isParsed('=FOO(BAR())', {
289
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
290
- arguments: [
291
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'BAR' },
292
- arguments: [] }
293
- ]
294
- });
295
- t.isParsed('=FOO(,)', {
296
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
297
- arguments: [ null, null ]
298
- });
299
- t.isParsed('=FOO(,,)', {
300
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
301
- arguments: [ null, null, null ]
302
- });
303
- t.isParsed('=FOO(1,)', {
304
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
305
- arguments: [ { type: 'Literal', value: 1, raw: '1' }, null ]
306
- });
307
- t.isParsed('=FOO(,1)', {
308
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' },
309
- arguments: [ null, { type: 'Literal', value: 1, raw: '1' } ]
310
- });
311
- t.isInvalidExpr('=FOO((1,2))');
312
- t.isInvalidExpr('=FOO(');
313
- t.isInvalidExpr('=FOO ()');
314
- t.isParsed('=FALSE()', {
315
- type: 'CallExpression', callee: { type: 'Identifier', name: 'FALSE' },
316
- arguments: []
317
- });
318
- t.isParsed('=TRUE()', {
319
- type: 'CallExpression', callee: { type: 'Identifier', name: 'TRUE' },
320
- arguments: []
321
- });
322
- t.end();
323
- });
324
-
325
- test('parse unary operator %', t => {
326
- t.isParsed('A1%', {
327
- type: 'UnaryExpression', operator: '%',
328
- arguments: [ { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' } ]
329
- });
330
- t.isParsed('1%', {
331
- type: 'UnaryExpression', operator: '%',
332
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
333
- });
334
- t.isParsed('(1)%', {
335
- type: 'UnaryExpression', operator: '%',
336
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
337
- });
338
- t.isInvalidExpr('%');
339
- t.end();
340
- });
341
-
342
- test('parse unary operator -', t => {
343
- t.isParsed('-1', { type: 'Literal', value: -1, raw: '-1' });
344
- t.isParsed('-1', {
345
- type: 'UnaryExpression', operator: '-',
346
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
347
- }, { negativeNumbers: false });
348
- t.isParsed('-"1"', {
349
- type: 'UnaryExpression', operator: '-',
350
- arguments: [ { type: 'Literal', value: '1', raw: '"1"' } ]
351
- });
352
- t.isParsed('-A1:B2', {
353
- type: 'UnaryExpression', operator: '-',
354
- arguments: [ { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' } ]
355
- });
356
- t.isParsed('--1', {
357
- type: 'UnaryExpression', operator: '-',
358
- arguments: [ { type: 'Literal', value: -1, raw: '-1' } ]
359
- });
360
- t.isParsed('--1', {
361
- type: 'UnaryExpression', operator: '-',
362
- arguments: [ {
363
- type: 'UnaryExpression', operator: '-',
364
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
365
- } ]
366
- }, { negativeNumbers: false });
367
- t.isInvalidExpr('--');
368
- t.isInvalidExpr('-');
369
- t.end();
370
- });
371
-
372
- test('parse unary operator +', t => {
373
- t.isParsed('+1', {
374
- type: 'UnaryExpression', operator: '+',
375
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
376
- });
377
- t.isParsed('+(1)', {
378
- type: 'UnaryExpression', operator: '+',
379
- arguments: [ { type: 'Literal', value: 1, raw: '1' } ]
380
- });
381
- t.isParsed('+"1"', {
382
- type: 'UnaryExpression', operator: '+',
383
- arguments: [ { type: 'Literal', value: '1', raw: '"1"' } ]
384
- });
385
- t.isParsed('+A1:B2', {
386
- type: 'UnaryExpression', operator: '+',
387
- arguments: [ { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' } ]
388
- });
389
- t.isInvalidExpr('++');
390
- t.isInvalidExpr('+');
391
- t.end();
392
- });
393
-
394
- test('parse unary operator #', t => {
395
- t.isParsed('D9#', {
396
- type: 'UnaryExpression', operator: '#',
397
- arguments: [ { type: 'ReferenceIdentifier', value: 'D9', kind: 'range' } ]
398
- });
399
- t.isParsed('A1:B2#', { // this parses but is a runtime error in Excel
400
- type: 'UnaryExpression', operator: '#',
401
- arguments: [ { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' } ]
402
- });
403
- t.isParsed('(A1):(B2)#', { // this parses but is a runtime error in Excel
404
- type: 'UnaryExpression', operator: '#',
405
- arguments: [ {
406
- type: 'BinaryExpression', operator: ':',
407
- arguments: [
408
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
409
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
410
- ] } ]
411
- });
412
- t.isParsed('(A1,B2)#', {
413
- type: 'UnaryExpression', operator: '#',
414
- arguments: [ {
415
- type: 'BinaryExpression', operator: ',',
416
- arguments: [
417
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
418
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
419
- ] } ]
420
- });
421
- t.isParsed('(A1 B2)#', {
422
- type: 'UnaryExpression', operator: '#',
423
- arguments: [ {
424
- type: 'BinaryExpression', operator: ' ',
425
- arguments: [
426
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
427
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
428
- ] } ]
429
- });
430
- t.isParsed('#REF!#', {
431
- type: 'UnaryExpression', operator: '#',
432
- arguments: [ { type: 'ErrorLiteral', value: '#REF!', raw: '#REF!' } ]
433
- });
434
- t.isParsed('INDIRECT("d9")#', {
435
- type: 'UnaryExpression', operator: '#',
436
- arguments: [ {
437
- type: 'CallExpression',
438
- callee: { type: 'Identifier', name: 'INDIRECT' },
439
- arguments: [ { type: 'Literal', value: 'd9', raw: '"d9"' } ]
440
- } ]
441
- });
442
- t.isInvalidExpr('1#');
443
- t.isInvalidExpr('"foo"#');
444
- t.isInvalidExpr('#A1');
445
- t.isInvalidExpr('##');
446
- t.isInvalidExpr('#VALUE!#');
447
- t.isInvalidExpr('#');
448
- t.isInvalidExpr('#A1');
449
- t.end();
450
- });
451
-
452
- test('parse unary operator @', t => {
453
- t.isParsed('@1', {
454
- type: 'UnaryExpression', operator: '@',
455
- arguments: [ { type: 'Literal', raw: '1', value: 1 } ]
456
- });
457
- t.isParsed('@"foo"', {
458
- type: 'UnaryExpression', operator: '@',
459
- arguments: [ { type: 'Literal', raw: '"foo"', value: 'foo' } ]
460
- });
461
- t.isParsed('@D9', {
462
- type: 'UnaryExpression', operator: '@',
463
- arguments: [ { type: 'ReferenceIdentifier', value: 'D9', kind: 'range' } ]
464
- });
465
- t.isParsed('@A1:B2', {
466
- type: 'UnaryExpression', operator: '@',
467
- arguments: [ { type: 'ReferenceIdentifier', value: 'A1:B2', kind: 'range' } ]
468
- });
469
- t.isParsed('@#REF!', {
470
- type: 'UnaryExpression', operator: '@',
471
- arguments: [ { type: 'ErrorLiteral', value: '#REF!', raw: '#REF!' } ]
472
- });
473
- t.isParsed('@FOO()', {
474
- type: 'UnaryExpression', operator: '@',
475
- arguments: [ {
476
- type: 'CallExpression',
477
- callee: { type: 'Identifier', name: 'FOO' },
478
- arguments: []
479
- } ]
480
- });
481
- t.isInvalidExpr('@');
482
- t.isInvalidExpr('@@');
483
- t.end();
484
- });
485
-
486
- // parse binary operators
487
- // FIXME: add precedence & associativity tests (2+3*4)
488
- [
489
- '+',
490
- '-',
491
- '^',
492
- '*',
493
- '/',
494
- '&',
495
- '=',
496
- '<',
497
- '>',
498
- '<=',
499
- '>=',
500
- '<>'
501
- ].forEach(op => {
502
- test('parse binary operator ' + op, t => {
503
- t.isParsed(`1${op}2`, {
504
- type: 'BinaryExpression', operator: op,
505
- arguments: [
506
- { type: 'Literal', value: 1, raw: '1' },
507
- { type: 'Literal', value: 2, raw: '2' }
508
- ]
509
- });
510
- t.isParsed(`1${op}2${op}3`, {
511
- type: 'BinaryExpression', operator: op,
512
- arguments: [
513
- { type: 'BinaryExpression', operator: op,
514
- arguments: [
515
- { type: 'Literal', value: 1, raw: '1' },
516
- { type: 'Literal', value: 2, raw: '2' }
517
- ] },
518
- { type: 'Literal', value: 3, raw: '3' }
519
- ]
520
- });
521
- t.isParsed(`"foo"${op}"bar"`, {
522
- type: 'BinaryExpression', operator: op,
523
- arguments: [
524
- { type: 'Literal', value: 'foo', raw: '"foo"' },
525
- { type: 'Literal', value: 'bar', raw: '"bar"' }
526
- ]
527
- });
528
- t.isParsed(`"foo"${op}"bar"`, {
529
- type: 'BinaryExpression', operator: op,
530
- arguments: [
531
- { type: 'Literal', value: 'foo', raw: '"foo"' },
532
- { type: 'Literal', value: 'bar', raw: '"bar"' }
533
- ]
534
- });
535
- t.isParsed(`{1,2}${op}{3,4}`, {
536
- type: 'BinaryExpression', operator: op,
537
- arguments: [
538
- { type: 'ArrayExpression', elements: [ [
539
- { type: 'Literal', value: 1, raw: '1' },
540
- { type: 'Literal', value: 2, raw: '2' }
541
- ] ] },
542
- { type: 'ArrayExpression', elements: [ [
543
- { type: 'Literal', value: 3, raw: '3' },
544
- { type: 'Literal', value: 4, raw: '4' }
545
- ] ] }
546
- ]
547
- });
548
- t.isParsed(`FOO()${op}BAR()`, {
549
- type: 'BinaryExpression', operator: op,
550
- arguments: [
551
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'FOO' }, arguments: [] },
552
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'BAR' }, arguments: [] }
553
- ]
554
- });
555
- t.isInvalidExpr(op);
556
- t.isInvalidExpr(op + op);
557
- t.isInvalidExpr('1' + op);
558
- if (op !== '+' && op !== '-') {
559
- t.isInvalidExpr('=' + op + '1');
560
- }
561
- t.end();
562
- });
563
- });
564
-
565
- // parse range operators
566
- [
567
- [ ':', 'range-join' ],
568
- [ ',', 'union' ],
569
- [ ' ', 'intersection' ]
570
- ].forEach(([ op, opName ]) => {
571
- test(`parse ${opName} operator "${op}"`, t => {
572
- t.isParsed(`named1${op}named2`, {
573
- type: 'BinaryExpression', operator: op,
574
- arguments: [
575
- { type: 'ReferenceIdentifier', value: 'named1', kind: 'name' },
576
- { type: 'ReferenceIdentifier', value: 'named2', kind: 'name' }
577
- ] });
578
- t.isParsed(`A1${op}named2`, {
579
- type: 'BinaryExpression', operator: op,
580
- arguments: [
581
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
582
- { type: 'ReferenceIdentifier', value: 'named2', kind: 'name' }
583
- ] });
584
- t.isParsed(`named1${op}B2`, {
585
- type: 'BinaryExpression', operator: op,
586
- arguments: [
587
- { type: 'ReferenceIdentifier', value: 'named1', kind: 'name' },
588
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
589
- ] });
590
- t.isParsed(`(A1)${op}(B2)`, {
591
- type: 'BinaryExpression', operator: op,
592
- arguments: [
593
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
594
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
595
- ] });
596
- t.isInvalidExpr(`A1${op}0`);
597
- t.isInvalidExpr(`0${op}A1`);
598
- t.isInvalidExpr(`0${op}0`);
599
- t.isInvalidExpr(`"foo"${op}"bar"`);
600
- t.isInvalidExpr(`TRUE${op}FALSE`);
601
- // REF! errors are a valid ref expression component
602
- t.isParsed(`A1${op}#REF!`, {
603
- type: 'BinaryExpression', operator: op,
604
- arguments: [
605
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
606
- { type: 'ErrorLiteral', value: '#REF!', raw: '#REF!' }
607
- ] });
608
- t.isParsed(`#REF!${op}B2`, {
609
- type: 'BinaryExpression', operator: op,
610
- arguments: [
611
- { type: 'ErrorLiteral', value: '#REF!', raw: '#REF!' },
612
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
613
- ] });
614
- t.isInvalidExpr(`A1${op}#NAME?`);
615
- t.isInvalidExpr(`A1${op}#VALUE!`);
616
- t.isInvalidExpr(`#NULL!${op}A1`);
617
-
618
- // union ops
619
- t.isParsed(`(A1,B2)${op}C3`, {
620
- type: 'BinaryExpression', operator: op,
621
- arguments: [
622
- { type: 'BinaryExpression', operator: ',',
623
- arguments: [
624
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
625
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
626
- ] },
627
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' }
628
- ] });
629
- t.isParsed(`C3${op}(A1,B2)`, {
630
- type: 'BinaryExpression', operator: op,
631
- arguments: [
632
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' },
633
- { type: 'BinaryExpression', operator: ',',
634
- arguments: [
635
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
636
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
637
- ] }
638
- ] });
639
- // intersection ops
640
- t.isParsed(`(A1 B2)${op}C3`, {
641
- type: 'BinaryExpression', operator: op,
642
- arguments: [
643
- { type: 'BinaryExpression', operator: ' ',
644
- arguments: [
645
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
646
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
647
- ] },
648
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' }
649
- ] });
650
- t.isParsed(`C3${op}(A1 B2)`, {
651
- type: 'BinaryExpression', operator: op,
652
- arguments: [
653
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' },
654
- { type: 'BinaryExpression', operator: ' ',
655
- arguments: [
656
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
657
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
658
- ] }
659
- ] });
660
- // join ops
661
- t.isParsed(`(A1:(B2))${op}C3`, {
662
- type: 'BinaryExpression', operator: op,
663
- arguments: [
664
- { type: 'BinaryExpression', operator: ':',
665
- arguments: [
666
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
667
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
668
- ] },
669
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' }
670
- ] });
671
- t.isParsed(`C3${op}(A1:(B2))`, {
672
- type: 'BinaryExpression', operator: op,
673
- arguments: [
674
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' },
675
- { type: 'BinaryExpression', operator: ':',
676
- arguments: [
677
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
678
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
679
- ] }
680
- ] });
681
- t.isParsed(`A1 ${op} B2`, {
682
- type: 'BinaryExpression', operator: op,
683
- arguments: [
684
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
685
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
686
- ] });
687
- // ref calls
688
- ([
689
- [ 'ANCHORARRAY', true ],
690
- [ 'CHOOSE', true ],
691
- [ 'DROP', true ],
692
- [ 'IF', true ],
693
- [ 'IFS', true ],
694
- [ 'INDEX', true ],
695
- [ 'INDIRECT', true ],
696
- // [ 'LAMBDA', true ],
697
- // [ 'LET', true ],
698
- [ 'OFFSET', true ],
699
- [ 'REDUCE', true ],
700
- [ 'SINGLE', true ],
701
- [ 'SWITCH', true ],
702
- [ 'TAKE', true ],
703
- [ 'XLOOKUP', true ],
704
- // non-ref functions
705
- [ 'CELL', false ],
706
- [ 'COUNT', false ],
707
- [ 'HSTACK', false ],
708
- [ 'N', false ],
709
- [ 'SUM', false ]
710
- ]).forEach(([ funcName, shouldWork ]) => {
711
- if (shouldWork) {
712
- t.isParsed(`${funcName}()${op}C3`, {
713
- type: 'BinaryExpression', operator: op,
714
- arguments: [
715
- { type: 'CallExpression', callee: { type: 'Identifier', name: funcName }, arguments: [] },
716
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' }
717
- ] });
718
- t.isParsed(`C3${op}${funcName}()`, {
719
- type: 'BinaryExpression', operator: op,
720
- arguments: [
721
- { type: 'ReferenceIdentifier', value: 'C3', kind: 'range' },
722
- { type: 'CallExpression', callee: { type: 'Identifier', name: funcName }, arguments: [] }
723
- ] });
724
- }
725
- else {
726
- t.isInvalidExpr(`${funcName}()${op}C3`);
727
- t.isInvalidExpr(`C3${op}${funcName}()`);
728
- }
729
- });
730
-
731
- t.end();
732
- });
733
- });
734
-
735
- test('union operators are normalized', t => {
736
- t.isParsed('A1 B2', {
737
- type: 'BinaryExpression', operator: ' ',
738
- arguments: [
739
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
740
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
741
- ]
742
- });
743
- t.isParsed('A1 B2', {
744
- type: 'BinaryExpression', operator: ' ',
745
- arguments: [
746
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
747
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
748
- ]
749
- });
750
- t.end();
751
- });
752
-
753
- test('does not tolerate unterminated tokens', t => {
754
- t.isInvalidExpr('="foo');
755
- t.end();
756
- });
757
-
758
- test('position information is correct', t => {
759
- t.isParsed(
760
- '=123.45',
761
- { type: 'Literal', value: 123.45, loc: [ 1, 7 ], raw: '123.45' },
762
- { withLocation: true }
763
- );
764
- t.isParsed(
765
- '="foo"',
766
- { type: 'Literal', value: 'foo', loc: [ 1, 6 ], raw: '"foo"' },
767
- { withLocation: true }
768
- );
769
- t.isParsed(
770
- '=true',
771
- { type: 'Literal', value: true, loc: [ 1, 5 ], raw: 'true' },
772
- { withLocation: true }
773
- );
774
- t.isParsed(
775
- '=Sheet1!A1:B2',
776
- { type: 'ReferenceIdentifier', value: 'Sheet1!A1:B2', kind: 'range', loc: [ 1, 13 ] },
777
- { withLocation: true }
778
- );
779
- t.isParsed(
780
- '=(#VALUE!)',
781
- { type: 'ErrorLiteral', value: '#VALUE!', loc: [ 2, 9 ], raw: '#VALUE!' },
782
- { withLocation: true }
783
- );
784
- // UnaryExpression
785
- t.isParsed(
786
- '=-A1',
787
- { type: 'UnaryExpression', loc: [ 1, 4 ], operator: '-', arguments: [
788
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range', loc: [ 2, 4 ] }
789
- ] },
790
- { withLocation: true }
791
- );
792
- t.isParsed(
793
- '=10%',
794
- { type: 'UnaryExpression', loc: [ 1, 4 ], operator: '%', arguments: [
795
- { type: 'Literal', value: 10, loc: [ 1, 3 ], raw: '10' }
796
- ] },
797
- { withLocation: true }
798
- );
799
- t.isParsed(
800
- '=-(123)',
801
- { type: 'UnaryExpression', loc: [ 1, 6 ], operator: '-', arguments: [
802
- { type: 'Literal', value: 123, loc: [ 3, 6 ], raw: '123' }
803
- ] },
804
- { withLocation: true }
805
- );
806
- t.isParsed(
807
- '(123+(234))',
808
- { type: 'BinaryExpression', loc: [ 1, 9 ], operator: '+', arguments: [
809
- { type: 'Literal', value: 123, loc: [ 1, 4 ], raw: '123' },
810
- { type: 'Literal', value: 234, loc: [ 6, 9 ], raw: '234' }
811
- ] },
812
- { withLocation: true }
813
- );
814
- t.isParsed(
815
- '=(A1 B2)',
816
- { type: 'BinaryExpression', loc: [ 2, 7 ], operator: ' ', arguments: [
817
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range', loc: [ 2, 4 ] },
818
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range', loc: [ 5, 7 ] }
819
- ] },
820
- { withLocation: true }
821
- );
822
- t.isParsed(
823
- '=SUM(4,5)',
824
- { type: 'CallExpression', loc: [ 1, 9 ],
825
- callee: { type: 'Identifier', name: 'SUM', loc: [ 1, 4 ] },
826
- arguments: [
827
- { type: 'Literal', value: 4, loc: [ 5, 6 ], raw: '4' },
828
- { type: 'Literal', value: 5, loc: [ 7, 8 ], raw: '5' }
829
- ] },
830
- { withLocation: true }
831
- );
832
- // ArrayExpression
833
- t.isParsed(
834
- '={ 1, 2; 3, 4 }',
835
- { type: 'ArrayExpression', loc: [ 1, 15 ], elements: [
836
- [ { type: 'Literal', value: 1, loc: [ 3, 4 ], raw: '1' },
837
- { type: 'Literal', value: 2, loc: [ 6, 7 ], raw: '2' } ],
838
- [ { type: 'Literal', value: 3, loc: [ 9, 10 ], raw: '3' },
839
- { type: 'Literal', value: 4, loc: [ 12, 13 ], raw: '4' } ]
840
- ] },
841
- { withLocation: true }
842
- );
843
- t.end();
844
- });
845
-
846
- test('does not tolerate unterminated tokens', t => {
847
- // whitespace in arrays
848
- t.isParsed('=SORT({ A:A, B:B })',
849
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'SORT' },
850
- arguments: [
851
- { type: 'ArrayExpression', elements: [
852
- [ { type: 'ReferenceIdentifier', value: 'A:A', kind: 'beam' },
853
- { type: 'ReferenceIdentifier', value: 'B:B', kind: 'beam' } ]
854
- ] }
855
- ] },
856
- { permitArrayRanges: true });
857
- // whitespace in arguments
858
- t.isParsed('=A2:A5=XLOOKUP(B1,C:C, D:D)',
859
- { type: 'BinaryExpression', operator: '=', arguments: [
860
- { type: 'ReferenceIdentifier', value: 'A2:A5', kind: 'range' },
861
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'XLOOKUP' },
862
- arguments: [
863
- { type: 'ReferenceIdentifier', value: 'B1', kind: 'range' },
864
- { type: 'ReferenceIdentifier', value: 'C:C', kind: 'beam' },
865
- { type: 'ReferenceIdentifier', value: 'D:D', kind: 'beam' }
866
- ] }
867
- ] },
868
- { permitArrayRanges: true });
869
- // whitespace surrounding comma
870
- t.isParsed('=SUM(12 , B:B)',
871
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'SUM' }, arguments: [
872
- { type: 'Literal', value: 12, raw: '12' },
873
- { type: 'ReferenceIdentifier', value: 'B:B', kind: 'beam' }
874
- ] },
875
- { permitArrayCalls: true });
876
- // whitespace tailing operator
877
- t.isParsed('=A:A= C1',
878
- { type: 'BinaryExpression', operator: '=', arguments: [
879
- { type: 'ReferenceIdentifier', value: 'A:A', kind: 'beam' },
880
- { type: 'ReferenceIdentifier', value: 'C1', kind: 'range' }
881
- ] },
882
- { permitArrayCalls: true });
883
- t.end();
884
- });
885
-
886
- test('parser can permit xlsx mode references', t => {
887
- t.isInvalidExpr('=SUM([Workbook.xlsx]!A1+[Workbook.xlsx]!Table1[#Data])');
888
- t.isParsed('=SUM([Workbook.xlsx]!A1+[Workbook.xlsx]!Table1[#Data])',
889
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'SUM' }, arguments: [
890
- { type: 'BinaryExpression', operator: '+', arguments: [
891
- { type: 'ReferenceIdentifier', value: '[Workbook.xlsx]!A1', kind: 'range' },
892
- { type: 'ReferenceIdentifier', value: '[Workbook.xlsx]!Table1[#Data]', kind: 'table' }
893
- ] }
894
- ] },
895
- { xlsx: true });
896
- t.end();
897
- });
898
-
899
- test('parser supports LAMBDA expressions', t => {
900
- t.isInvalidExpr('LAMBDA(,)');
901
- t.isInvalidExpr('LAMBDA(a,)');
902
- t.isInvalidExpr('LAMBDA(a,,)');
903
- t.isInvalidExpr('=LAMBDA(1,1)');
904
- t.isInvalidExpr('=LAMBDA(a,1,a)');
905
- t.isInvalidExpr('=LAMBDA(a,A,1)');
906
- t.isInvalidExpr('=LAMBDA(a,a,1)');
907
- t.isInvalidExpr('=LAMBDA(A1,B1,1)');
908
- t.isParsed('=LAMBDA()', {
909
- type: 'LambdaExpression',
910
- params: [],
911
- body: null
912
- });
913
- t.isParsed('=LAMBDA(1)', {
914
- type: 'LambdaExpression',
915
- params: [],
916
- body: {
917
- type: 'Literal',
918
- value: 1,
919
- raw: '1'
920
- }
921
- });
922
- t.isParsed('=LAMBDA(1+1)', {
923
- type: 'LambdaExpression',
924
- params: [],
925
- body: {
926
- type: 'BinaryExpression',
927
- operator: '+',
928
- arguments: [
929
- { type: 'Literal', value: 1, raw: '1' },
930
- { type: 'Literal', value: 1, raw: '1' }
931
- ]
932
- }
933
- });
934
- t.isParsed('=LAMBDA(a,1)', {
935
- type: 'LambdaExpression',
936
- body: { type: 'Literal', value: 1, raw: '1' },
937
- params: [
938
- { type: 'Identifier', name: 'a' }
939
- ]
940
- });
941
- t.isParsed('=LAMBDA(a,b,1)', {
942
- type: 'LambdaExpression',
943
- body: { type: 'Literal', value: 1, raw: '1' },
944
- params: [
945
- { type: 'Identifier', name: 'a' },
946
- { type: 'Identifier', name: 'b' }
947
- ]
948
- });
949
- t.isParsed('=LAMBDA(a,b,1)', {
950
- type: 'LambdaExpression',
951
- body: { type: 'Literal', value: 1, raw: '1' },
952
- params: [
953
- { type: 'Identifier', name: 'a' },
954
- { type: 'Identifier', name: 'b' }
955
- ]
956
- });
957
- t.isParsed('=lambda(a,b,a*b)', {
958
- type: 'LambdaExpression',
959
- body: {
960
- type: 'BinaryExpression',
961
- operator: '*',
962
- arguments: [
963
- { type: 'ReferenceIdentifier', value: 'a', kind: 'name' },
964
- { type: 'ReferenceIdentifier', value: 'b', kind: 'name' }
965
- ]
966
- },
967
- params: [
968
- { type: 'Identifier', name: 'a' },
969
- { type: 'Identifier', name: 'b' }
970
- ]
971
- });
972
- t.isParsed('=lambda(a,b,a*b)', {
973
- type: 'LambdaExpression',
974
- body: {
975
- type: 'BinaryExpression',
976
- operator: '*',
977
- arguments: [
978
- { type: 'ReferenceIdentifier', value: 'a', kind: 'name' },
979
- { type: 'ReferenceIdentifier', value: 'b', kind: 'name' }
980
- ]
981
- },
982
- params: [
983
- { type: 'Identifier', name: 'a' },
984
- { type: 'Identifier', name: 'b' }
985
- ]
986
- });
987
- t.isParsed('=lambda( a , b , a b )', {
988
- type: 'LambdaExpression',
989
- body: {
990
- type: 'BinaryExpression',
991
- operator: ' ',
992
- arguments: [
993
- { type: 'ReferenceIdentifier', value: 'a', kind: 'name' },
994
- { type: 'ReferenceIdentifier', value: 'b', kind: 'name' }
995
- ]
996
- },
997
- params: [
998
- { type: 'Identifier', name: 'a' },
999
- { type: 'Identifier', name: 'b' }
1000
- ]
1001
- });
1002
- // r and c are forbidden as names, but should work here
1003
- t.isParsed('=LAMBDA(r,c,r*c)', {
1004
- type: 'LambdaExpression',
1005
- body: {
1006
- type: 'BinaryExpression',
1007
- operator: '*',
1008
- arguments: [
1009
- { type: 'ReferenceIdentifier', value: 'r', kind: 'name' },
1010
- { type: 'ReferenceIdentifier', value: 'c', kind: 'name' }
1011
- ]
1012
- },
1013
- params: [
1014
- { type: 'Identifier', name: 'r' },
1015
- { type: 'Identifier', name: 'c' }
1016
- ]
1017
- });
1018
- t.end();
1019
- });
1020
-
1021
- test('parser allows calling refs, lambda, let, and call expressions', t => {
1022
- t.isInvalidExpr('1()');
1023
- t.isInvalidExpr('"str"()');
1024
- t.isInvalidExpr('#VALUE!()');
1025
- t.isInvalidExpr('foo%()');
1026
- t.isInvalidExpr('foo ()');
1027
-
1028
- t.isParsed('=lambda()()', {
1029
- type: 'CallExpression',
1030
- callee: {
1031
- type: 'LambdaExpression',
1032
- params: [],
1033
- body: null
1034
- },
1035
- arguments: []
1036
- });
1037
- t.isParsed('=lambda(1)(1)', {
1038
- type: 'CallExpression',
1039
- callee: {
1040
- type: 'LambdaExpression',
1041
- params: [],
1042
- body: { type: 'Literal', value: 1, raw: '1' }
1043
- },
1044
- arguments: [
1045
- { type: 'Literal', value: 1, raw: '1' }
1046
- ]
1047
- });
1048
- t.isParsed('=FOO()()', {
1049
- type: 'CallExpression',
1050
- callee: {
1051
- type: 'CallExpression',
1052
- callee: { type: 'Identifier', name: 'FOO' },
1053
- arguments: []
1054
- },
1055
- arguments: []
1056
- });
1057
- t.isParsed('=(A1)()', {
1058
- type: 'CallExpression',
1059
- callee: { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
1060
- arguments: []
1061
- });
1062
- t.isParsed('=LET(a,1,a)()', {
1063
- type: 'CallExpression',
1064
- callee: {
1065
- type: 'LetExpression',
1066
- declarations: [
1067
- { type: 'LetDeclarator',
1068
- id: { type: 'Identifier', name: 'a' },
1069
- init: { type: 'Literal', value: 1, raw: '1' }
1070
- }
1071
- ],
1072
- body: { type: 'ReferenceIdentifier', value: 'a', kind: 'name' }
1073
- },
1074
- arguments: []
1075
- });
1076
- t.isParsed('=#REF!()', {
1077
- type: 'CallExpression',
1078
- callee: {
1079
- type: 'ErrorLiteral',
1080
- value: '#REF!',
1081
- raw: '#REF!'
1082
- },
1083
- arguments: []
1084
- });
1085
- // this is allowed because in Excel: `foo#()` is really `ANCHORARRAY(foo)()`
1086
- t.isParsed('foo#()', {
1087
- type: 'CallExpression',
1088
- callee: {
1089
- type: 'UnaryExpression', operator: '#', arguments: [
1090
- { type: 'ReferenceIdentifier', value: 'foo', kind: 'name' }
1091
- ]
1092
- },
1093
- arguments: []
1094
- });
1095
- // `@1()` works in Excel because it is really `SINGLE(foo)()`
1096
- t.end();
1097
- });
1098
-
1099
- test('parser supports LET expressions', t => {
1100
- // Argument is not a name
1101
- t.isInvalidExpr('LET(,)');
1102
- t.isInvalidExpr('LET(1,a,1)');
1103
- // Unexpected end of arguments
1104
- t.isInvalidExpr('LET()');
1105
- t.isInvalidExpr('LET(a,b)');
1106
- t.isInvalidExpr('LET(a,)');
1107
- // Unexpected argument following calculation
1108
- t.isInvalidExpr('LET(a,a,1,1)');
1109
- t.isInvalidExpr('LET(a,a,1,a)');
1110
- t.isInvalidExpr('LET(a,1,b,1,c,1,a1+b,1)');
1111
- // Duplicate name: a
1112
- t.isInvalidExpr('LET(a,1,a,1,1)');
1113
- t.isInvalidExpr('LET(a,1,A,1,1)');
1114
-
1115
- t.isParsed('=LET(a,1,)', {
1116
- type: 'LetExpression',
1117
- declarations: [
1118
- {
1119
- type: 'LetDeclarator',
1120
- id: { type: 'Identifier', name: 'a' },
1121
- init: { type: 'Literal', value: 1, raw: '1' }
1122
- }
1123
- ],
1124
- body: null
1125
- });
1126
- t.isParsed('=LET(a,1,a)', {
1127
- type: 'LetExpression',
1128
- declarations: [
1129
- {
1130
- type: 'LetDeclarator',
1131
- id: { type: 'Identifier', name: 'a' },
1132
- init: { type: 'Literal', value: 1, raw: '1' }
1133
- }
1134
- ],
1135
- body: { type: 'ReferenceIdentifier', value: 'a', kind: 'name' }
1136
- });
1137
- t.isParsed('=LET(a,1,b,1,c,1,a+b*c)', {
1138
- type: 'LetExpression',
1139
- declarations: [
1140
- { type: 'LetDeclarator',
1141
- id: { type: 'Identifier', name: 'a' },
1142
- init: { type: 'Literal', value: 1, raw: '1' } },
1143
- { type: 'LetDeclarator',
1144
- id: { type: 'Identifier', name: 'b' },
1145
- init: { type: 'Literal', value: 1, raw: '1' } },
1146
- { type: 'LetDeclarator',
1147
- id: { type: 'Identifier', name: 'c' },
1148
- init: { type: 'Literal', value: 1, raw: '1' } }
1149
- ],
1150
- body: {
1151
- type: 'BinaryExpression', operator: '+', arguments: [
1152
- { type: 'ReferenceIdentifier', value: 'a', kind: 'name' },
1153
- { type: 'BinaryExpression', operator: '*', arguments: [
1154
- { type: 'ReferenceIdentifier', value: 'b', kind: 'name' },
1155
- { type: 'ReferenceIdentifier', value: 'c', kind: 'name' }
1156
- ] }
1157
- ]
1158
- }
1159
- });
1160
- // r and c are forbidden as names, but should work here
1161
- t.isParsed('LET(r,1,c,1,r*c)', {
1162
- type: 'LetExpression',
1163
- declarations: [
1164
- { type: 'LetDeclarator',
1165
- id: { type: 'Identifier', name: 'r' },
1166
- init: { type: 'Literal', value: 1, raw: '1' }
1167
- },
1168
- {
1169
- type: 'LetDeclarator',
1170
- id: { type: 'Identifier', name: 'c' },
1171
- init: { type: 'Literal', value: 1, raw: '1' }
1172
- }
1173
- ],
1174
- body: {
1175
- type: 'BinaryExpression',
1176
- operator: '*',
1177
- arguments: [
1178
- { type: 'ReferenceIdentifier', value: 'r', kind: 'name' },
1179
- { type: 'ReferenceIdentifier', value: 'c', kind: 'name' }
1180
- ]
1181
- }
1182
- });
1183
- t.end();
1184
- });
1185
-
1186
- test('parser whitespace handling', t => {
1187
- t.isParsed('\tA1\u00a0+\nB2\r', {
1188
- type: 'BinaryExpression',
1189
- operator: '+',
1190
- arguments: [
1191
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
1192
- { type: 'ReferenceIdentifier', value: 'B2', kind: 'range' }
1193
- ]
1194
- });
1195
- t.end();
1196
- });
1197
-
1198
- test('looseRefCalls: true relaxes ref function restrictions', t => {
1199
- t.isInvalidExpr('A1:TESTFN()');
1200
- t.isParsed('A1:TESTFN()', {
1201
- type: 'BinaryExpression', operator: ':',
1202
- arguments: [
1203
- { type: 'ReferenceIdentifier', value: 'A1', kind: 'range' },
1204
- { type: 'CallExpression', callee: { type: 'Identifier', name: 'TESTFN' }, arguments: [] }
1205
- ]
1206
- }, { looseRefCalls: true });
1207
- t.end();
1208
- });