@borgar/fx
Version:
Utilities for working with Excel formulas
167 lines (155 loc) • 9.02 kB
text/typescript
import { describe, test, expect } from 'vitest';
import { FX_PREFIX, OPERATOR, NUMBER, REF_RANGE, REF_BEAM, FUNCTION, WHITESPACE, REF_STRUCT } from './constants.ts';
import { addTokenMeta } from './addTokenMeta.ts';
import { tokenize } from './tokenize.ts';
function isMetaTokens (expr: string, expected: any[], context?: any, opts?: any) {
const actual = addTokenMeta(tokenize(expr, opts), context);
if (actual.length === expected.length) {
actual.forEach((d, i) => {
const keys = Object.keys(d).concat(Object.keys(expected[i]));
keys.forEach(key => {
if (actual[i][key] === expected[i][key]) {
delete actual[i][key];
delete expected[i][key];
}
});
});
}
expect(actual).toEqual(expected);
}
describe('add extra meta to operators', () => {
test('parens should be grouped and tagged with depth', () => {
isMetaTokens('=((1)+(1))', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 1, type: OPERATOR, value: '(', groupId: 'fxg3' },
{ index: 2, depth: 2, type: OPERATOR, value: '(', groupId: 'fxg1' },
{ index: 3, depth: 2, type: NUMBER, value: '1' },
{ index: 4, depth: 2, type: OPERATOR, value: ')', groupId: 'fxg1' },
{ index: 5, depth: 1, type: OPERATOR, value: '+' },
{ index: 6, depth: 2, type: OPERATOR, value: '(', groupId: 'fxg2' },
{ index: 7, depth: 2, type: NUMBER, value: '1' },
{ index: 8, depth: 2, type: OPERATOR, value: ')', groupId: 'fxg2' },
{ index: 9, depth: 1, type: OPERATOR, value: ')', groupId: 'fxg3' }
]);
});
test('don\'t be fooled by imbalanced parens', () => {
isMetaTokens('=)())', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: OPERATOR, value: ')', error: true },
{ index: 2, depth: 1, type: OPERATOR, value: '(', groupId: 'fxg1' },
{ index: 3, depth: 1, type: OPERATOR, value: ')', groupId: 'fxg1' },
{ index: 4, depth: 0, type: OPERATOR, value: ')', error: true }
]);
});
test('don\'t be fooled by nested curlys', () => {
isMetaTokens('={{}}', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 1, type: OPERATOR, value: '{', groupId: 'fxg1' },
{ index: 2, depth: 1, type: OPERATOR, value: '{', error: true },
{ index: 3, depth: 1, type: OPERATOR, value: '}', groupId: 'fxg1' },
{ index: 4, depth: 0, type: OPERATOR, value: '}', error: true }
]);
});
test('group ranges if they are equivalent', () => {
isMetaTokens("=B11,B11:B12,'Sheet11'!B11,SHEET1!$B11,sheet1!$b$11,A1:B11,[foo]Sheet1!B11,'[foo]Sheet1'!B11", [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: REF_RANGE, value: 'B11', groupId: 'fxg1' },
{ index: 2, depth: 0, type: OPERATOR, value: ',' },
{ index: 3, depth: 0, type: REF_RANGE, value: 'B11:B12', groupId: 'fxg2' },
{ index: 4, depth: 0, type: OPERATOR, value: ',' },
{ index: 5, depth: 0, type: REF_RANGE, value: "'Sheet11'!B11", groupId: 'fxg3' },
{ index: 6, depth: 0, type: OPERATOR, value: ',' },
{ index: 7, depth: 0, type: REF_RANGE, value: 'SHEET1!$B11', groupId: 'fxg1' },
{ index: 8, depth: 0, type: OPERATOR, value: ',' },
{ index: 9, depth: 0, type: REF_RANGE, value: 'sheet1!$b$11', groupId: 'fxg1' },
{ index: 10, depth: 0, type: OPERATOR, value: ',' },
{ index: 11, depth: 0, type: REF_RANGE, value: 'A1:B11', groupId: 'fxg4' },
{ index: 12, depth: 0, type: OPERATOR, value: ',' },
{ index: 13, depth: 0, type: REF_RANGE, value: '[foo]Sheet1!B11', groupId: 'fxg1' },
{ index: 14, depth: 0, type: OPERATOR, value: ',' },
{ index: 15, depth: 0, type: REF_RANGE, value: "'[foo]Sheet1'!B11", groupId: 'fxg1' }
], { sheetName: 'Sheet1', workbookName: 'foo' });
});
test('group beam references', () => {
isMetaTokens('=A:A,1:1,Sheet1!A:A:1:1,[foo]Sheet1!1:1', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: REF_BEAM, value: 'A:A', groupId: 'fxg1' },
{ index: 2, depth: 0, type: OPERATOR, value: ',' },
{ index: 3, depth: 0, type: REF_BEAM, value: '1:1', groupId: 'fxg2' },
{ index: 4, depth: 0, type: OPERATOR, value: ',' },
{ index: 5, depth: 0, type: REF_BEAM, value: 'Sheet1!A:A', groupId: 'fxg1' },
{ index: 6, depth: 0, type: OPERATOR, value: ':' },
{ index: 7, depth: 0, type: REF_BEAM, value: '1:1', groupId: 'fxg2' },
{ index: 8, depth: 0, type: OPERATOR, value: ',' },
{ index: 9, depth: 0, type: REF_BEAM, value: '[foo]Sheet1!1:1', groupId: 'fxg2' }
], { sheetName: 'Sheet1', workbookName: 'foo' });
});
test('complex function with nested grouping', () => {
isMetaTokens('=SUM((1, 2), {3, 4})', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: FUNCTION, value: 'SUM' },
{ index: 2, depth: 1, type: OPERATOR, value: '(', groupId: 'fxg3' },
{ index: 3, depth: 2, type: OPERATOR, value: '(', groupId: 'fxg1' },
{ index: 4, depth: 2, type: NUMBER, value: '1' },
{ index: 5, depth: 2, type: OPERATOR, value: ',' },
{ index: 6, depth: 2, type: WHITESPACE, value: ' ' },
{ index: 7, depth: 2, type: NUMBER, value: '2' },
{ index: 8, depth: 2, type: OPERATOR, value: ')', groupId: 'fxg1' },
{ index: 9, depth: 1, type: OPERATOR, value: ',' },
{ index: 10, depth: 1, type: WHITESPACE, value: ' ' },
{ index: 11, depth: 2, type: OPERATOR, value: '{', groupId: 'fxg2' },
{ index: 12, depth: 2, type: NUMBER, value: '3' },
{ index: 13, depth: 2, type: OPERATOR, value: ',' },
{ index: 14, depth: 2, type: WHITESPACE, value: ' ' },
{ index: 15, depth: 2, type: NUMBER, value: '4' },
{ index: 16, depth: 2, type: OPERATOR, value: '}', groupId: 'fxg2' },
{ index: 17, depth: 1, type: OPERATOR, value: ')', groupId: 'fxg3' }
], { sheetName: 'Sheet1', workbookName: 'foo' });
});
test('group structured references', () => {
isMetaTokens('=table[#all]+table[foobar]+table[[#All]]', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: REF_STRUCT, value: 'table[#all]', groupId: 'fxg1' },
{ index: 2, depth: 0, type: OPERATOR, value: '+' },
{ index: 3, depth: 0, type: REF_STRUCT, value: 'table[foobar]', groupId: 'fxg2' },
{ index: 4, depth: 0, type: OPERATOR, value: '+' },
{ index: 5, depth: 0, type: REF_STRUCT, value: 'table[[#All]]', groupId: 'fxg1' }
], { sheetName: 'Sheet1', workbookName: 'foo' });
});
test('group workbook references in xlsx mode', () => {
isMetaTokens('=[foo]!A1+[foo]Sheet1!A1+Sheet1!A1+A1', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: REF_RANGE, value: '[foo]!A1', groupId: 'fxg1' },
{ index: 2, depth: 0, type: OPERATOR, value: '+' },
{ index: 3, depth: 0, type: REF_RANGE, value: '[foo]Sheet1!A1', groupId: 'fxg1' },
{ index: 4, depth: 0, type: OPERATOR, value: '+' },
{ index: 5, depth: 0, type: REF_RANGE, value: 'Sheet1!A1', groupId: 'fxg1' },
{ index: 6, depth: 0, type: OPERATOR, value: '+' },
{ index: 7, depth: 0, type: REF_RANGE, value: 'A1', groupId: 'fxg1' }
], { sheetName: 'Sheet1', workbookName: 'foo' }, { xlsx: true });
});
test('group structured references in xlsx mode', () => {
isMetaTokens('=[foo]!table[#data]+[foo]Sheet1!table[#data]+Sheet1!table[#data]+table[#data]', [
{ index: 0, depth: 0, type: FX_PREFIX, value: '=' },
{ index: 1, depth: 0, type: REF_STRUCT, value: '[foo]!table[#data]', groupId: 'fxg1' },
{ index: 2, depth: 0, type: OPERATOR, value: '+' },
{ index: 3, depth: 0, type: REF_STRUCT, value: '[foo]Sheet1!table[#data]', groupId: 'fxg1' },
{ index: 4, depth: 0, type: OPERATOR, value: '+' },
{ index: 5, depth: 0, type: REF_STRUCT, value: 'Sheet1!table[#data]', groupId: 'fxg1' },
{ index: 6, depth: 0, type: OPERATOR, value: '+' },
{ index: 7, depth: 0, type: REF_STRUCT, value: 'table[#data]', groupId: 'fxg1' }
], { sheetName: 'Sheet1', workbookName: 'foo' }, { xlsx: true });
});
test('trimming should not affect range equivalency', () => {
isMetaTokens('=A1:B2*A1.:B2*A1:.B2*A1.:.B2', [
{ type: FX_PREFIX, value: '=', index: 0, depth: 0 },
{ type: REF_RANGE, value: 'A1:B2', index: 1, depth: 0, groupId: 'fxg1' },
{ type: OPERATOR, value: '*', index: 2, depth: 0 },
{ type: REF_RANGE, value: 'A1.:B2', index: 3, depth: 0, groupId: 'fxg1' },
{ type: OPERATOR, value: '*', index: 4, depth: 0 },
{ type: REF_RANGE, value: 'A1:.B2', index: 5, depth: 0, groupId: 'fxg1' },
{ type: OPERATOR, value: '*', index: 6, depth: 0 },
{ type: REF_RANGE, value: 'A1.:.B2', index: 7, depth: 0, groupId: 'fxg1' }
], { sheetName: 'Sheet1', workbookName: 'foo' }, { xlsx: true });
});
});