@borgar/fx 3.0.0 → 4.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,144 @@
1
+ # Abstract Syntax Tree Format
2
+
3
+ This document specifies the core AST node types that support the Excel grammar. The format is based on the [ESTree AST](https://github.com/estree/estree) (and so is this document).
4
+
5
+ ## Node objects
6
+
7
+ All AST nodes are represented by `Node` objects. They may have any prototype inheritance but implement the following basic interface:
8
+
9
+ ```
10
+ interface Node {
11
+ type: string;
12
+ loc?: Location | null;
13
+ }
14
+ ```
15
+
16
+ The `type` field is a string representing the AST variant type. Each subtype of Node is documented below with the specific string of its `type` field. You can use this field to determine which interface a node implements.
17
+
18
+ The `loc` field represents the source location information of the node. If the node contains no information about the source location, the field is `null`; otherwise it is an array consisting of a two numbers: A start offset (the position of the first character of the parsed source region) and an end offset (the position of the first character after the parsed source region):
19
+
20
+ ```
21
+ interface Location extends Array<number> {
22
+ 0: number;
23
+ 1: number;
24
+ }
25
+ ```
26
+
27
+ ## Identifier
28
+
29
+ ```
30
+ interface Identifier extends Node {
31
+ type: "Identifier";
32
+ name: string;
33
+ }
34
+ ```
35
+
36
+ An identifier. These only appear on `CallExpression` and will always be a static string representing the name of a function call.
37
+
38
+ ## ReferenceIdentifier
39
+
40
+ ```
41
+ interface ReferenceIdentifier extends Node {
42
+ type: "ReferenceIdentifier";
43
+ value: string;
44
+ }
45
+ ```
46
+
47
+ A range identifier.
48
+
49
+
50
+ ## Literal
51
+
52
+ ```
53
+ interface Literal extends Node {
54
+ type: "Literal";
55
+ raw: string;
56
+ value: string | number | boolean;
57
+ }
58
+ ```
59
+
60
+ A literal token. Captures numbers, strings, and booleans. Literal errors have their own variant type.
61
+
62
+ ## ErrorLiteral
63
+
64
+ ```
65
+ interface ErrorLiteral extends Node {
66
+ type: "ErrorLiteral";
67
+ raw: string;
68
+ value: string;
69
+ }
70
+ ```
71
+
72
+ An Error expression.
73
+
74
+ ## UnaryExpression
75
+
76
+ ```
77
+ interface UnaryExpression extends Node {
78
+ type: "UnaryExpression";
79
+ operator: UnaryOperator;
80
+ arguments: Array<[ Node ]>;
81
+ }
82
+ ```
83
+
84
+ A unary operator expression.
85
+
86
+ ### UnaryOperator
87
+
88
+ ```
89
+ type UnaryOperator = (
90
+ "+" | "-" | "%" | "#" | "@"
91
+ )
92
+ ```
93
+
94
+ A unary operator token.
95
+
96
+ ## BinaryExpression
97
+
98
+ ```
99
+ interface BinaryExpression extends Node {
100
+ type: "BinaryExpression";
101
+ operator: BinaryOperator;
102
+ arguments: Array<[ Node, Node ]>;
103
+ }
104
+ ```
105
+
106
+ A binary operator expression.
107
+
108
+ ### BinaryOperator
109
+
110
+ ```
111
+ type BinaryOperator = (
112
+ "=" | "<" | ">" | "<=" | ">=" | "<>" |
113
+ "-" | "+" | "*" | "/" | "^" |
114
+ ":" | " " | "," |
115
+ "&"
116
+ )
117
+ ```
118
+
119
+ A binary operator token. Note that Excels union operator is whitespace so a parser must normalize this to a single space.
120
+
121
+ ## CallExpression
122
+
123
+ ```
124
+ interface CallExpression extends Node {
125
+ type: "CallExpression";
126
+ callee: Identifier;
127
+ arguments: Array<Node>;
128
+ }
129
+ ```
130
+
131
+ A function call expression.
132
+
133
+ ## ArrayExpression
134
+
135
+ ```
136
+ interface ArrayExpression extends Node {
137
+ type: "ArrayExpression";
138
+ elements: Array<Array<Literal | Error | ReferenceIdentifier>>;
139
+ }
140
+ ```
141
+
142
+ An array expression. Excel does not have empty or sparse arrays and restricts array elements to literals. Google Sheets allows `ReferenceIdentifier`s as elements of arrays, the fx parser as an option for this but it is off by default.
143
+
144
+
@@ -0,0 +1,60 @@
1
+ # References and Ranges
2
+
3
+ In Excels spreadsheet formula language terminology, a reference is similar to what is in most programming is called a variable. Spreadsheets do not have variables though, they have cells. The cells can be referenced in formulas, either directly (such as `=SUM(A1)`), or through aliases (such as `=SUM(someName)`).
4
+
5
+ A range is when a cell, or a set of cells, is referenced directly. Ranges in formulas can come in one of two syntax styles: The commonly known A1 style, as well as R1C1 style where both axes are numerical. Only one style can be used at a time in a formula.
6
+
7
+ ## Ranges
8
+
9
+ This tokenizer considers there to be three "types" of ranges:
10
+
11
+ ### Ranges (`REF_RANGE`)
12
+
13
+ The basic type of range will be referencing either:
14
+
15
+ * A single cell, like `A1` or `AF31`.
16
+ * A bounded rectangle of cells, like `A1:B2` or `AF17:AF31`.
17
+
18
+ ### Range ternary (`REF_TERNARY`)
19
+
20
+ Ternary ranges are rectangles of cells defined by only three of the four possible sides. They are are unbounded in either bottom or right dimension:
21
+
22
+ * A rectangle of cells that is unbounded to the bottom, like `A1:A` or `C3:D`.
23
+ * A rectangle of cells that is unbounded to the right, like `A1:1` or `F2:5`.
24
+
25
+ This type of range is not supported in Excel, so it is an opt-in for the tokenizer (see [README.md](./README.md)).
26
+
27
+ ### Range beams (`REF_BEAM`)
28
+
29
+ Range beams are rectangles of cells that are unbounded in either left and right, or top and bottom dimensions.
30
+
31
+ * A rectangle of cells that is unbounded to the top and bottom, like `A:A` or `C:D`.
32
+ * A rectangle of cells that is unbounded to the left and right, like `1:1` or `2:5`.
33
+
34
+
35
+ ## References
36
+
37
+ As well as ranges, a reference can be either of:
38
+
39
+ ### Named Ranges or Names (`REF_NAMED`)
40
+
41
+ [Named Ranges][named], or Names as Excel prefers to call them most of the time, are usually a references to a range or table but may also be references to entire expressions.
42
+
43
+ ### Structured references (`REF_STRUCT`)
44
+
45
+ [Structured references][srefs] are references into an area or slice of a table. They can include the table name (which is essentially a Name), as well as either or both of a section directive and/or column name or a range of columns.
46
+
47
+ * A structured reference to a column: `Table1[Column1]`
48
+ * A structured reference to the tables totals: `Table1[#Totals]`
49
+ * A structured reference to a range of columns: `[[Column1]:[Column3]]`
50
+ * A structured reference to a specific column of a tables totals: `Table1[[#Totals],[Column2]]`
51
+
52
+ ---
53
+
54
+ Spreadsheet applications will normalize all ranges when you enter a formula, flipping the left/right and top/bottom coordinates as needed to keep the range top to bottom and left to right. Structured references will also be normalized as appropriate.
55
+
56
+ The library has tools to both normalize the ranges, as well as filling in the missing boundaries (see [README.md](./README.md)).
57
+
58
+
59
+ [named]: https://support.microsoft.com/en-us/office/define-and-use-names-in-formulas-4d0f13ac-53b7-422e-afd2-abd7ff379c64
60
+ [srefs]: https://support.microsoft.com/en-us/office/using-structured-references-with-excel-tables-f5ed2452-2337-4f71-bed3-c8ae6d2b276e
package/lib/a1.js CHANGED
@@ -2,8 +2,23 @@ import { MAX_ROWS, MAX_COLS } from './constants.js';
2
2
  import { parseRef } from './parseRef.js';
3
3
  import { stringifyPrefix } from './stringifyPrefix.js';
4
4
 
5
- export function fromCol (columnId) {
6
- const x = (columnId || '');
5
+ const clamp = (min, val, max) => Math.min(Math.max(val, min), max);
6
+ const toColStr = (c, a) => (a ? '$' : '') + toCol(c);
7
+ const toRowStr = (r, a) => (a ? '$' : '') + toRow(r);
8
+
9
+ /**
10
+ * Convert a column string representation to a 0 based
11
+ * offset number (`"C"` = `2`).
12
+ *
13
+ * The method expects a valid column identifier made up of _only_
14
+ * A-Z letters, which may be either upper or lower case. Other input will
15
+ * return garbage.
16
+ *
17
+ * @param {string} columnString The column string identifier
18
+ * @return {number} Zero based column index number
19
+ */
20
+ export function fromCol (columnString) {
21
+ const x = (columnString || '');
7
22
  const l = x.length;
8
23
  let n = 0;
9
24
  if (l > 2) {
@@ -24,11 +39,21 @@ export function fromCol (columnId) {
24
39
  return n;
25
40
  }
26
41
 
27
- export function toCol (left) {
42
+ /**
43
+ * Convert a 0 based offset number to a column string
44
+ * representation (`2` = `"C"`).
45
+ *
46
+ * The method expects a number between 0 and 16383. Other input will
47
+ * return garbage.
48
+ *
49
+ * @param {number} columnIndex Zero based column index number
50
+ * @return {string} The column string identifier
51
+ */
52
+ export function toCol (columnIndex) {
28
53
  return (
29
- (left >= 702 ? String.fromCharCode((((left - 702) / 676) - 0) % 26 + 65) : '') +
30
- (left >= 26 ? String.fromCharCode(Math.floor(((left / 26) - 1) % 26 + 65)) : '') +
31
- String.fromCharCode((left % 26 + 65))
54
+ (columnIndex >= 702 ? String.fromCharCode((((columnIndex - 702) / 676) - 0) % 26 + 65) : '') +
55
+ (columnIndex >= 26 ? String.fromCharCode(Math.floor(((columnIndex / 26) - 1) % 26 + 65)) : '') +
56
+ String.fromCharCode((columnIndex % 26 + 65))
32
57
  );
33
58
  }
34
59
 
@@ -50,15 +75,32 @@ export function toAbsolute (range) {
50
75
  return { top, left, bottom, right, $left: true, $right: true, $top: true, $bottom: true };
51
76
  }
52
77
 
53
- // TODO: add a setting for partials?
54
- const toColStr = (c, a) => (a ? '$' : '') + toCol(c);
55
- const toRowStr = (r, a) => (a ? '$' : '') + toRow(r);
78
+ /**
79
+ * Stringify a range object into A1 syntax.
80
+ *
81
+ * @private
82
+ * @see parseA1Ref
83
+ * @param {Object} range A range object
84
+ * @return {string} An A1-style string represenation of a range
85
+ */
56
86
  export function toA1 (range) {
57
- const { top, left, bottom, right, $left, $right, $top, $bottom } = range;
87
+ let { top, left, bottom, right } = range;
88
+ const { $left, $right, $top, $bottom } = range;
58
89
  const noLeft = left == null;
59
90
  const noRight = right == null;
60
91
  const noTop = top == null;
61
92
  const noBottom = bottom == null;
93
+ // allow skipping right and bottom to define a cell
94
+ top = clamp(0, top | 0, MAX_ROWS);
95
+ left = clamp(0, left | 0, MAX_COLS);
96
+ if (!noLeft && !noTop && noRight && noBottom) {
97
+ bottom = top;
98
+ right = left;
99
+ }
100
+ else {
101
+ bottom = clamp(0, bottom | 0, MAX_ROWS);
102
+ right = clamp(0, right | 0, MAX_COLS);
103
+ }
62
104
  // A:A
63
105
  if ((top === 0 && bottom >= MAX_ROWS) || (noTop && noBottom)) {
64
106
  return toColStr(left, $left) + ':' + toColStr(right, $right);
@@ -84,8 +126,9 @@ export function toA1 (range) {
84
126
  return toColStr(right, $right) + toRowStr(top, $top) + ':' + toRowStr(bottom, $bottom);
85
127
  }
86
128
  // A1:A1
87
- else if (right !== left || bottom !== top || $right !== $left || $bottom !== $top) {
88
- return toColStr(left, $left) + toRowStr(top, $top) + ':' + toColStr(right, $right) + toRowStr(bottom, $bottom);
129
+ if (right !== left || bottom !== top || $right !== $left || $bottom !== $top) {
130
+ return toColStr(left, $left) + toRowStr(top, $top) + ':' +
131
+ toColStr(right, $right) + toRowStr(bottom, $bottom);
89
132
  }
90
133
  // A1
91
134
  return toColStr(left, $left) + toRowStr(top, $top);
@@ -104,6 +147,15 @@ function splitA1 (str) {
104
147
  ];
105
148
  }
106
149
 
150
+ /**
151
+ * Parse a simple string reference to an A1 range into a range object.
152
+ * Will accept `A1`, `A2`, `A:A`, or `1:1`.
153
+ *
154
+ * @private
155
+ * @see parseA1Ref
156
+ * @param {string} rangeString A range string
157
+ * @return {(Object|null)} An object representing a valid reference or null if it is invalid.
158
+ */
107
159
  export function fromA1 (rangeStr) {
108
160
  let top = null;
109
161
  let left = null;
@@ -165,8 +217,37 @@ export function fromA1 (rangeStr) {
165
217
  return { top, left, bottom, right, $top, $left, $bottom, $right };
166
218
  }
167
219
 
168
- export function parseA1Ref (ref, { allowNamed = true, allowTernary = false } = {}) {
169
- const d = parseRef(ref, { allowNamed, allowTernary, r1c1: false });
220
+ /**
221
+ * Parse a string reference into an object representing it.
222
+ *
223
+ * ```js
224
+ * parseA1Ref('Sheet1!A$1:$B2');
225
+ * // => {
226
+ * // context: [ 'Sheet1' ],
227
+ * // range: {
228
+ * // top: 0,
229
+ * // left: 0,
230
+ * // bottom: 1,
231
+ * // right: 1
232
+ * // $top: true,
233
+ * // $left: false,
234
+ * // $bottom: false,
235
+ * // $right: true
236
+ * // }
237
+ * // }
238
+ * ```
239
+ *
240
+ * For A:A or A1:A style ranges, `null` will be used for any dimensions that the
241
+ * syntax does not specify:
242
+ *
243
+ * @param {string} refString An A1-style reference string
244
+ * @param {Object} [options={}] Options
245
+ * @param {boolean} [options.allowNamed=true] Enable parsing names as well as ranges.
246
+ * @param {boolean} [options.allowTernary=false] Enables the recognition of ternary ranges in the style of `A1:A` or `A1:1`. These are supported by Google Sheets but not Excel. See: References.md.
247
+ * @return {(Object|null)} An object representing a valid reference or null if it is invalid.
248
+ */
249
+ export function parseA1Ref (refString, { allowNamed = true, allowTernary = false } = {}) {
250
+ const d = parseRef(refString, { allowNamed, allowTernary, r1c1: false });
170
251
  if (d && (d.r0 || d.name)) {
171
252
  let range = null;
172
253
  if (d.r0) {
@@ -185,13 +266,70 @@ export function parseA1Ref (ref, { allowNamed = true, allowTernary = false } = {
185
266
  return null;
186
267
  }
187
268
 
188
- export function stringifyA1Ref (ref) {
189
- return stringifyPrefix(ref) + (
190
- ref.name ? ref.name : toA1(ref.range)
269
+ /**
270
+ * Get an A1-style string representation of a reference object.
271
+ *
272
+ * ```js
273
+ * stringifyA1Ref({
274
+ * context: [ 'Sheet1' ],
275
+ * range: {
276
+ * top: 0,
277
+ * left: 0,
278
+ * bottom: 1,
279
+ * right: 1,
280
+ * $top: true,
281
+ * $left: false,
282
+ * $bottom: false,
283
+ * $right: true
284
+ * }
285
+ * });
286
+ * // => 'Sheet1!A$1:$B2'
287
+ * ```
288
+ *
289
+ * @param {Object} refObject A reference object
290
+ * @return {Object} The reference in A1-style string format
291
+ */
292
+ export function stringifyA1Ref (refObject) {
293
+ return stringifyPrefix(refObject) + (
294
+ refObject.name ? refObject.name : toA1(refObject.range)
191
295
  );
192
296
  }
193
297
 
194
- export function addRangeBounds (range) {
298
+ /**
299
+ * Fill the any missing bounds in range objects. Top will be set to 0, bottom to
300
+ * 1048575, left to 0, and right to 16383, if they are `null` or `undefined`.
301
+ *
302
+ * ```js
303
+ * addA1RangeBounds({
304
+ * context: [ 'Sheet1' ],
305
+ * range: {
306
+ * top: 0,
307
+ * left: 0,
308
+ * bottom: 1,
309
+ * $top: true,
310
+ * $left: false,
311
+ * $bottom: false,
312
+ * }
313
+ * });
314
+ * // => {
315
+ * // context: [ 'Sheet1' ],
316
+ * // range: {
317
+ * // top: 0,
318
+ * // left: 0,
319
+ * // bottom: 1,
320
+ * // right: 16383,
321
+ * // $top: true,
322
+ * // $left: false,
323
+ * // $bottom: false,
324
+ * // $right: false
325
+ * // }
326
+ * // }
327
+ * ```
328
+ *
329
+ * @param {Object} range The range part of a reference object.
330
+ * @return {Object} same range with missing bounds filled in.
331
+ */
332
+ export function addA1RangeBounds (range) {
195
333
  if (range.top == null) {
196
334
  range.top = 0;
197
335
  range.$top = false;
@@ -210,15 +348,3 @@ export function addRangeBounds (range) {
210
348
  }
211
349
  return range;
212
350
  }
213
-
214
- export default {
215
- fromCol,
216
- toCol,
217
- toRelative,
218
- toAbsolute,
219
- to: toA1,
220
- from: fromA1,
221
- parse: parseA1Ref,
222
- addBounds: addRangeBounds,
223
- stringify: stringifyA1Ref
224
- };
package/lib/a1.spec.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  } from './a1.js';
14
14
  import { MAX_COLS, MAX_ROWS } from './constants.js';
15
15
 
16
- Test.prototype.isA1Equal = function isTokens (expr, expect, opts) {
16
+ Test.prototype.isA1Equal = function isA1Equal (expr, expect, opts) {
17
17
  if (expect) {
18
18
  expect = {
19
19
  context: [],
@@ -224,7 +224,14 @@ test('A1 serialization', t => {
224
224
  t.is(toA1({ top: 0, right: 3, bottom: 0 }), 'D1:1', '1:D1 → D1:1');
225
225
  t.is(toA1({ top: 0, left: 3, bottom: 0, $top: true }), 'D$1:1', 'D$1:1');
226
226
  t.is(toA1({ top: 0, left: 3, bottom: 0, $left: true }), '$D1:1', '$D1:1');
227
- t.is(toA1({ top: 0, left: 3, bottom: 0, $bottom: true }), 'D1:$1', 'D1:$1');
227
+ t.is(toA1({ top: 0, left: 3, bottom: 0, $left: true }), '$D1:1', '$D1:1');
228
+ // allow skipping right/bottom for cells
229
+ t.is(toA1({ top: 0, left: 0 }), 'A1', 'A1');
230
+ // clamp the range at min/max dimensions
231
+ t.is(toA1({ top: -10, bottom: -5, left: -10, right: -5 }), 'A1', 'A1');
232
+ t.is(toA1({ top: 15e5, bottom: 15e5, left: 20000, right: 20000 }), 'XFD1048576', 'XFD1048576');
233
+ t.is(toA1({ top: 2, bottom: 2, left: 2.5, right: 2.5 }), 'C3', 'C3');
234
+ t.is(toA1({ top: 1.5, bottom: 2.5, left: 4.5, right: 8.5 }), 'E2:I3', 'E2:I3');
228
235
  t.end();
229
236
  });
230
237
 
@@ -1,4 +1,4 @@
1
- import { RANGE, RANGE_BEAM, RANGE_TERNARY, UNKNOWN } from './constants.js';
1
+ import { REF_RANGE, REF_BEAM, REF_TERNARY, UNKNOWN } from './constants.js';
2
2
  import { parseA1Ref } from './a1.js';
3
3
 
4
4
  function getIDer () {
@@ -46,8 +46,53 @@ function isEquivalent (refA, refB) {
46
46
  return true;
47
47
  }
48
48
 
49
- // when context is Sheet1, we should consider Sheet!A1 == A1
50
- export function addMeta (tokens, { sheetName = '', workbookName = '' } = {}) {
49
+ /**
50
+ * Runs through a list of tokens and adds extra attributes such as matching
51
+ * parens and ranges.
52
+ *
53
+ * The `context` parameter defines default reference attributes:
54
+ * `{ workbookName: 'report.xlsx', sheetName: 'Sheet1' }`.
55
+ * If supplied, these are used to match `A1` to `Sheet1!A1`.
56
+ *
57
+ * All tokens will be tagged with a `.depth` number value to indicating the
58
+ * level of nesting in parentheses as well as an `.index` number indicating
59
+ * their zero based position in the list.
60
+ *
61
+ * The returned output will be the same array of tokens but the following
62
+ * properties will added to tokens (as applicable):
63
+ *
64
+ * #### Parentheses ( )
65
+ *
66
+ * Matching parens will be tagged with `.groupId` string identifier as well as
67
+ * a `.depth` number value (indicating the level of nesting).
68
+ *
69
+ * Closing parens without a counterpart will be tagged with `.error`
70
+ * (boolean true).
71
+ *
72
+ * #### Curly brackets { }
73
+ *
74
+ * Matching curly brackets will be tagged with `.groupId` string identifier.
75
+ * These may not be nested in Excel.
76
+ *
77
+ * Closing curly brackets without a counterpart will be tagged with `.error`
78
+ * (boolean `true`).
79
+ *
80
+ * #### Ranges (`REF_RANGE` or `REF_BEAM` type tokens)
81
+ *
82
+ * All ranges will be tagged with `.groupId` string identifier regardless of
83
+ * the number of times they occur.
84
+ *
85
+ * #### Tokens of type `UNKNOWN`
86
+ *
87
+ * All will be tagged with `.error` (boolean `true`).
88
+ *
89
+ * @param {Array<Object>} tokenlist An array of tokens (from `tokenize()`)
90
+ * @param {Object} [context={}] A contest used to match `A1` to `Sheet1!A1`.
91
+ * @param {string} [context.sheetName=''] An implied sheet name ('Sheet1')
92
+ * @param {string} [context.workbookName=''] An implied workbook name ('report.xlsx')
93
+ * @return {Array<Object>} The input array with the enchanced tokens
94
+ */
95
+ export function addTokenMeta (tokens, { sheetName = '', workbookName = '' } = {}) {
51
96
  const parenStack = [];
52
97
  let arrayStart = null;
53
98
  const uid = getIDer();
@@ -95,8 +140,8 @@ export function addMeta (tokens, { sheetName = '', workbookName = '' } = {}) {
95
140
  }
96
141
  arrayStart = null;
97
142
  }
98
- else if (token.type === RANGE || token.type === RANGE_BEAM || token.type === RANGE_TERNARY) {
99
- const ref = parseA1Ref(token.value, { allowNamed: false, allowTernary: true });
143
+ else if (token.type === REF_RANGE || token.type === REF_BEAM || token.type === REF_TERNARY) {
144
+ const ref = parseA1Ref(token.value, { allowTernary: true });
100
145
  if (ref && ref.range) {
101
146
  ref.source = token.value;
102
147
  if (!ref.context.length) {
@@ -1,10 +1,10 @@
1
1
  import { test, Test } from 'tape';
2
- import { FX_PREFIX, OPERATOR, NUMBER, RANGE, RANGE_BEAM, FUNCTION, WHITESPACE } from './constants.js';
3
- import { addMeta } from './addMeta.js';
2
+ import { FX_PREFIX, OPERATOR, NUMBER, REF_RANGE, REF_BEAM, FUNCTION, WHITESPACE } from './constants.js';
3
+ import { addTokenMeta } from './addTokenMeta.js';
4
4
  import { tokenize } from './lexer.js';
5
5
 
6
6
  Test.prototype.isMetaTokens = function isTokens (expr, expect, opts) {
7
- const actual = addMeta(tokenize(expr), opts);
7
+ const actual = addTokenMeta(tokenize(expr), opts);
8
8
  if (actual.length === expect.length) {
9
9
  actual.forEach((d, i) => {
10
10
  const keys = Object.keys(d).concat(Object.keys(expect[i]));
@@ -55,34 +55,34 @@ test('add extra meta to operators', t => {
55
55
  // group ranges if they are equivalent
56
56
  t.isMetaTokens("=B11,B11:B12,'Sheet11'!B11,SHEET1!$B11,sheet1!$b$11,A1:B11,[foo]Sheet1!B11,'[foo]Sheet1'!B11", [
57
57
  { index: 0, depth: 0, type: FX_PREFIX, value: '=' },
58
- { index: 1, depth: 0, type: RANGE, value: 'B11', groupId: 'fxg1' },
58
+ { index: 1, depth: 0, type: REF_RANGE, value: 'B11', groupId: 'fxg1' },
59
59
  { index: 2, depth: 0, type: OPERATOR, value: ',' },
60
- { index: 3, depth: 0, type: RANGE, value: 'B11:B12', groupId: 'fxg2' },
60
+ { index: 3, depth: 0, type: REF_RANGE, value: 'B11:B12', groupId: 'fxg2' },
61
61
  { index: 4, depth: 0, type: OPERATOR, value: ',' },
62
- { index: 5, depth: 0, type: RANGE, value: "'Sheet11'!B11", groupId: 'fxg3' },
62
+ { index: 5, depth: 0, type: REF_RANGE, value: "'Sheet11'!B11", groupId: 'fxg3' },
63
63
  { index: 6, depth: 0, type: OPERATOR, value: ',' },
64
- { index: 7, depth: 0, type: RANGE, value: 'SHEET1!$B11', groupId: 'fxg1' },
64
+ { index: 7, depth: 0, type: REF_RANGE, value: 'SHEET1!$B11', groupId: 'fxg1' },
65
65
  { index: 8, depth: 0, type: OPERATOR, value: ',' },
66
- { index: 9, depth: 0, type: RANGE, value: 'sheet1!$b$11', groupId: 'fxg1' },
66
+ { index: 9, depth: 0, type: REF_RANGE, value: 'sheet1!$b$11', groupId: 'fxg1' },
67
67
  { index: 10, depth: 0, type: OPERATOR, value: ',' },
68
- { index: 11, depth: 0, type: RANGE, value: 'A1:B11', groupId: 'fxg4' },
68
+ { index: 11, depth: 0, type: REF_RANGE, value: 'A1:B11', groupId: 'fxg4' },
69
69
  { index: 12, depth: 0, type: OPERATOR, value: ',' },
70
- { index: 13, depth: 0, type: RANGE, value: '[foo]Sheet1!B11', groupId: 'fxg1' },
70
+ { index: 13, depth: 0, type: REF_RANGE, value: '[foo]Sheet1!B11', groupId: 'fxg1' },
71
71
  { index: 14, depth: 0, type: OPERATOR, value: ',' },
72
- { index: 15, depth: 0, type: RANGE, value: "'[foo]Sheet1'!B11", groupId: 'fxg1' }
72
+ { index: 15, depth: 0, type: REF_RANGE, value: "'[foo]Sheet1'!B11", groupId: 'fxg1' }
73
73
  ], { sheetName: 'Sheet1', workbookName: 'foo' });
74
74
 
75
75
  t.isMetaTokens('=A:A,1:1,Sheet1!A:A:1:1,[foo]Sheet1!1:1', [
76
76
  { index: 0, depth: 0, type: FX_PREFIX, value: '=' },
77
- { index: 1, depth: 0, type: RANGE_BEAM, value: 'A:A', groupId: 'fxg1' },
77
+ { index: 1, depth: 0, type: REF_BEAM, value: 'A:A', groupId: 'fxg1' },
78
78
  { index: 2, depth: 0, type: OPERATOR, value: ',' },
79
- { index: 3, depth: 0, type: RANGE_BEAM, value: '1:1', groupId: 'fxg2' },
79
+ { index: 3, depth: 0, type: REF_BEAM, value: '1:1', groupId: 'fxg2' },
80
80
  { index: 4, depth: 0, type: OPERATOR, value: ',' },
81
- { index: 5, depth: 0, type: RANGE_BEAM, value: 'Sheet1!A:A', groupId: 'fxg1' },
81
+ { index: 5, depth: 0, type: REF_BEAM, value: 'Sheet1!A:A', groupId: 'fxg1' },
82
82
  { index: 6, depth: 0, type: OPERATOR, value: ':' },
83
- { index: 7, depth: 0, type: RANGE_BEAM, value: '1:1', groupId: 'fxg2' },
83
+ { index: 7, depth: 0, type: REF_BEAM, value: '1:1', groupId: 'fxg2' },
84
84
  { index: 8, depth: 0, type: OPERATOR, value: ',' },
85
- { index: 9, depth: 0, type: RANGE_BEAM, value: '[foo]Sheet1!1:1', groupId: 'fxg2' }
85
+ { index: 9, depth: 0, type: REF_BEAM, value: '[foo]Sheet1!1:1', groupId: 'fxg2' }
86
86
  ], { sheetName: 'Sheet1', workbookName: 'foo' });
87
87
 
88
88
  t.isMetaTokens('=SUM((1, 2), {3, 4})', [
package/lib/constants.js CHANGED
@@ -8,12 +8,22 @@ export const WHITESPACE = 'whitespace';
8
8
  export const STRING = 'string';
9
9
  export const CONTEXT_QUOTE = 'context_quote';
10
10
  export const CONTEXT = 'context';
11
- export const RANGE = 'range';
12
- export const RANGE_BEAM = 'range_beam';
13
- export const RANGE_TERNARY = 'range_ternary';
14
- export const RANGE_NAMED = 'range_named';
11
+ export const REF_RANGE = 'range';
12
+ export const REF_BEAM = 'range_beam';
13
+ export const REF_TERNARY = 'range_ternary';
14
+ export const REF_NAMED = 'range_named';
15
+ export const REF_STRUCT = 'structured';
15
16
  export const FX_PREFIX = 'fx_prefix';
16
17
  export const UNKNOWN = 'unknown';
17
18
 
19
+ export const UNARY = 'UnaryExpression';
20
+ export const BINARY = 'BinaryExpression';
21
+ export const REFERENCE = 'ReferenceIdentifier';
22
+ export const LITERAL = 'Literal';
23
+ export const ERROR_LITERAL = 'ErrorLiteral';
24
+ export const CALL = 'CallExpression';
25
+ export const ARRAY = 'ArrayExpression';
26
+ export const IDENTIFIER = 'Identifier';
27
+
18
28
  export const MAX_COLS = 2 ** 14 - 1; // 16383
19
29
  export const MAX_ROWS = 2 ** 20 - 1; // 1048575