@borgar/fx 2.1.1 → 3.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.
@@ -1,9 +1,13 @@
1
1
  import { test, Test } from 'tape';
2
- import { FX_PREFIX, OPERATOR, BOOLEAN, ERROR, NUMBER, FUNCTION, WHITESPACE, STRING, PATH_PREFIX, PATH_QUOTE, PATH_BRACE, RANGE, RANGE_BEAM, RANGE_NAMED } from './constants.js';
2
+ import {
3
+ FX_PREFIX, UNKNOWN,
4
+ OPERATOR, BOOLEAN, ERROR, NUMBER, FUNCTION, WHITESPACE, STRING,
5
+ RANGE, RANGE_BEAM, RANGE_NAMED, RANGE_TERNARY, CONTEXT, CONTEXT_QUOTE, NEWLINE
6
+ } from './constants.js';
3
7
  import { tokenize } from './lexer.js';
4
8
 
5
9
  Test.prototype.isTokens = function isTokens (expr, result, opts) {
6
- this.deepEqual(tokenize(expr, (opts || {})), result, expr);
10
+ this.deepEqual(tokenize(expr, { negativeNumbers: false, ...opts }), result, expr);
7
11
  };
8
12
  Test.prototype.isTokensNeg = function isTokensNeg (expr, result, opts) {
9
13
  this.deepEqual(tokenize(expr, { ...opts, negativeNumbers: true }), result, expr);
@@ -106,7 +110,7 @@ test('tokenize operators', t => {
106
110
  ]);
107
111
  t.isTokens('=Sheet1!A1', [
108
112
  { type: FX_PREFIX, value: '=' },
109
- { type: PATH_PREFIX, value: 'Sheet1' },
113
+ { type: CONTEXT, value: 'Sheet1' },
110
114
  { type: OPERATOR, value: '!' },
111
115
  { type: RANGE, value: 'A1' }
112
116
  ], { mergeRanges: false });
@@ -229,6 +233,12 @@ test('tokenize functions', t => {
229
233
  { type: NUMBER, value: '1' },
230
234
  { type: OPERATOR, value: ')' }
231
235
  ]);
236
+ t.isTokens('=N()', [
237
+ { type: FX_PREFIX, value: '=' },
238
+ { type: FUNCTION, value: 'N' },
239
+ { type: OPERATOR, value: '(' },
240
+ { type: OPERATOR, value: ')' }
241
+ ]);
232
242
  t.isTokens('=@SUM(1)', [
233
243
  { type: FX_PREFIX, value: '=' },
234
244
  { type: OPERATOR, value: '@' },
@@ -307,9 +317,9 @@ test('tokenize functions', t => {
307
317
  { type: NUMBER, value: '1' },
308
318
  { type: OPERATOR, value: ')' }
309
319
  ]);
310
- t.isTokens('=.FOO(1)', [
320
+ t.isTokens('=\\FOO(1)', [
311
321
  { type: FX_PREFIX, value: '=' },
312
- { type: RANGE_NAMED, value: '.FOO' },
322
+ { type: RANGE_NAMED, value: '\\FOO' },
313
323
  { type: OPERATOR, value: '(' },
314
324
  { type: NUMBER, value: '1' },
315
325
  { type: OPERATOR, value: ')' }
@@ -425,7 +435,7 @@ test('tokenize negative numbers', t => {
425
435
  { type: NUMBER, value: '-1.5E-10' }
426
436
  ]);
427
437
  t.isTokensNeg('-1', [
428
- { type: 'number', value: '-1' }
438
+ { type: NUMBER, value: '-1' }
429
439
  ]);
430
440
 
431
441
  //
@@ -436,144 +446,144 @@ test('tokenize negative numbers', t => {
436
446
  { type: NUMBER, value: '1' }
437
447
  ]);
438
448
  t.isTokensNeg('1--1', [
439
- { type: 'number', value: '1' },
440
- { type: 'operator', value: '-' },
441
- { type: 'number', value: '-1' }
449
+ { type: NUMBER, value: '1' },
450
+ { type: OPERATOR, value: '-' },
451
+ { type: NUMBER, value: '-1' }
442
452
  ]);
443
453
  t.isTokensNeg('1 - -1', [
444
- { type: 'number', value: '1' },
445
- { type: 'whitespace', value: ' ' },
446
- { type: 'operator', value: '-' },
447
- { type: 'whitespace', value: ' ' },
448
- { type: 'number', value: '-1' }
454
+ { type: NUMBER, value: '1' },
455
+ { type: WHITESPACE, value: ' ' },
456
+ { type: OPERATOR, value: '-' },
457
+ { type: WHITESPACE, value: ' ' },
458
+ { type: NUMBER, value: '-1' }
449
459
  ]);
450
460
  t.isTokensNeg('1 - - 1', [
451
- { type: 'number', value: '1' },
452
- { type: 'whitespace', value: ' ' },
453
- { type: 'operator', value: '-' },
454
- { type: 'whitespace', value: ' ' },
455
- { type: 'operator', value: '-' },
456
- { type: 'whitespace', value: ' ' },
457
- { type: 'number', value: '1' }
461
+ { type: NUMBER, value: '1' },
462
+ { type: WHITESPACE, value: ' ' },
463
+ { type: OPERATOR, value: '-' },
464
+ { type: WHITESPACE, value: ' ' },
465
+ { type: OPERATOR, value: '-' },
466
+ { type: WHITESPACE, value: ' ' },
467
+ { type: NUMBER, value: '1' }
458
468
  ]);
459
469
  t.isTokensNeg('1 \n - \n -1', [
460
- { type: 'number', value: '1' },
461
- { type: 'whitespace', value: ' ' },
462
- { type: 'newline', value: '\n' },
463
- { type: 'whitespace', value: ' ' },
464
- { type: 'operator', value: '-' },
465
- { type: 'whitespace', value: ' ' },
466
- { type: 'newline', value: '\n' },
467
- { type: 'whitespace', value: ' ' },
468
- { type: 'number', value: '-1' }
470
+ { type: NUMBER, value: '1' },
471
+ { type: WHITESPACE, value: ' ' },
472
+ { type: NEWLINE, value: '\n' },
473
+ { type: WHITESPACE, value: ' ' },
474
+ { type: OPERATOR, value: '-' },
475
+ { type: WHITESPACE, value: ' ' },
476
+ { type: NEWLINE, value: '\n' },
477
+ { type: WHITESPACE, value: ' ' },
478
+ { type: NUMBER, value: '-1' }
469
479
  ]);
470
480
  t.isTokensNeg('-(-1)', [
471
- { type: 'operator', value: '-' },
472
- { type: 'operator', value: '(' },
473
- { type: 'number', value: '-1' },
474
- { type: 'operator', value: ')' }
481
+ { type: OPERATOR, value: '-' },
482
+ { type: OPERATOR, value: '(' },
483
+ { type: NUMBER, value: '-1' },
484
+ { type: OPERATOR, value: ')' }
475
485
  ]);
476
486
  t.isTokensNeg('-( -1 )', [
477
- { type: 'operator', value: '-' },
478
- { type: 'operator', value: '(' },
479
- { type: 'whitespace', value: ' ' },
480
- { type: 'number', value: '-1' },
481
- { type: 'whitespace', value: ' ' },
482
- { type: 'operator', value: ')' }
487
+ { type: OPERATOR, value: '-' },
488
+ { type: OPERATOR, value: '(' },
489
+ { type: WHITESPACE, value: ' ' },
490
+ { type: NUMBER, value: '-1' },
491
+ { type: WHITESPACE, value: ' ' },
492
+ { type: OPERATOR, value: ')' }
483
493
  ]);
484
494
 
485
495
  t.isTokensNeg('=true-1', [
486
- { type: 'fx-prefix', value: '=' },
487
- { type: 'bool', value: 'true' },
488
- { type: 'operator', value: '-' },
489
- { type: 'number', value: '1' }
496
+ { type: FX_PREFIX, value: '=' },
497
+ { type: BOOLEAN, value: 'true' },
498
+ { type: OPERATOR, value: '-' },
499
+ { type: NUMBER, value: '1' }
490
500
  ]);
491
501
  t.isTokensNeg('=true -1', [
492
- { type: 'fx-prefix', value: '=' },
493
- { type: 'bool', value: 'true' },
494
- { type: 'whitespace', value: ' ' },
495
- { type: 'operator', value: '-' },
496
- { type: 'number', value: '1' }
502
+ { type: FX_PREFIX, value: '=' },
503
+ { type: BOOLEAN, value: 'true' },
504
+ { type: WHITESPACE, value: ' ' },
505
+ { type: OPERATOR, value: '-' },
506
+ { type: NUMBER, value: '1' }
497
507
  ]);
498
508
  t.isTokensNeg('=true - 1', [
499
- { type: 'fx-prefix', value: '=' },
500
- { type: 'bool', value: 'true' },
501
- { type: 'whitespace', value: ' ' },
502
- { type: 'operator', value: '-' },
503
- { type: 'whitespace', value: ' ' },
504
- { type: 'number', value: '1' }
509
+ { type: FX_PREFIX, value: '=' },
510
+ { type: BOOLEAN, value: 'true' },
511
+ { type: WHITESPACE, value: ' ' },
512
+ { type: OPERATOR, value: '-' },
513
+ { type: WHITESPACE, value: ' ' },
514
+ { type: NUMBER, value: '1' }
505
515
  ]);
506
516
  t.isTokensNeg('=#VALUE!-1', [
507
- { type: 'fx-prefix', value: '=' },
508
- { type: 'error', value: '#VALUE!' },
509
- { type: 'operator', value: '-' },
510
- { type: 'number', value: '1' }
517
+ { type: FX_PREFIX, value: '=' },
518
+ { type: ERROR, value: '#VALUE!' },
519
+ { type: OPERATOR, value: '-' },
520
+ { type: NUMBER, value: '1' }
511
521
  ]);
512
522
  t.isTokensNeg('=#VALUE! -1', [
513
- { type: 'fx-prefix', value: '=' },
514
- { type: 'error', value: '#VALUE!' },
515
- { type: 'whitespace', value: ' ' },
516
- { type: 'operator', value: '-' },
517
- { type: 'number', value: '1' }
523
+ { type: FX_PREFIX, value: '=' },
524
+ { type: ERROR, value: '#VALUE!' },
525
+ { type: WHITESPACE, value: ' ' },
526
+ { type: OPERATOR, value: '-' },
527
+ { type: NUMBER, value: '1' }
518
528
  ]);
519
529
  t.isTokensNeg('=SUM(-1) -1', [
520
- { type: 'fx-prefix', value: '=' },
521
- { type: 'function', value: 'SUM' },
522
- { type: 'operator', value: '(' },
523
- { type: 'number', value: '-1' },
524
- { type: 'operator', value: ')' },
525
- { type: 'whitespace', value: ' ' },
526
- { type: 'operator', value: '-' },
527
- { type: 'number', value: '1' }
530
+ { type: FX_PREFIX, value: '=' },
531
+ { type: FUNCTION, value: 'SUM' },
532
+ { type: OPERATOR, value: '(' },
533
+ { type: NUMBER, value: '-1' },
534
+ { type: OPERATOR, value: ')' },
535
+ { type: WHITESPACE, value: ' ' },
536
+ { type: OPERATOR, value: '-' },
537
+ { type: NUMBER, value: '1' }
528
538
  ]);
529
539
  t.isTokensNeg('=SUM( -1)-1', [
530
- { type: 'fx-prefix', value: '=' },
531
- { type: 'function', value: 'SUM' },
532
- { type: 'operator', value: '(' },
533
- { type: 'whitespace', value: ' ' },
534
- { type: 'number', value: '-1' },
535
- { type: 'operator', value: ')' },
536
- { type: 'operator', value: '-' },
537
- { type: 'number', value: '1' }
540
+ { type: FX_PREFIX, value: '=' },
541
+ { type: FUNCTION, value: 'SUM' },
542
+ { type: OPERATOR, value: '(' },
543
+ { type: WHITESPACE, value: ' ' },
544
+ { type: NUMBER, value: '-1' },
545
+ { type: OPERATOR, value: ')' },
546
+ { type: OPERATOR, value: '-' },
547
+ { type: NUMBER, value: '1' }
538
548
  ]);
539
549
  t.isTokensNeg('=A1-1', [
540
- { type: 'fx-prefix', value: '=' },
541
- { type: 'range', value: 'A1' },
542
- { type: 'operator', value: '-' },
543
- { type: 'number', value: '1' }
550
+ { type: FX_PREFIX, value: '=' },
551
+ { type: RANGE, value: 'A1' },
552
+ { type: OPERATOR, value: '-' },
553
+ { type: NUMBER, value: '1' }
544
554
  ]);
545
555
  t.isTokensNeg('=A1 -1', [
546
- { type: 'fx-prefix', value: '=' },
547
- { type: 'range', value: 'A1' },
548
- { type: 'whitespace', value: ' ' },
549
- { type: 'operator', value: '-' },
550
- { type: 'number', value: '1' }
556
+ { type: FX_PREFIX, value: '=' },
557
+ { type: RANGE, value: 'A1' },
558
+ { type: WHITESPACE, value: ' ' },
559
+ { type: OPERATOR, value: '-' },
560
+ { type: NUMBER, value: '1' }
551
561
  ]);
552
562
  t.isTokensNeg('=foo-1', [
553
- { type: 'fx-prefix', value: '=' },
554
- { type: 'range-named', value: 'foo' },
555
- { type: 'operator', value: '-' },
556
- { type: 'number', value: '1' }
563
+ { type: FX_PREFIX, value: '=' },
564
+ { type: RANGE_NAMED, value: 'foo' },
565
+ { type: OPERATOR, value: '-' },
566
+ { type: NUMBER, value: '1' }
557
567
  ]);
558
568
  t.isTokensNeg('=foo -1', [
559
- { type: 'fx-prefix', value: '=' },
560
- { type: 'range-named', value: 'foo' },
561
- { type: 'whitespace', value: ' ' },
562
- { type: 'operator', value: '-' },
563
- { type: 'number', value: '1' }
569
+ { type: FX_PREFIX, value: '=' },
570
+ { type: RANGE_NAMED, value: 'foo' },
571
+ { type: WHITESPACE, value: ' ' },
572
+ { type: OPERATOR, value: '-' },
573
+ { type: NUMBER, value: '1' }
564
574
  ]);
565
575
  t.isTokensNeg('="true"-1', [
566
- { type: 'fx-prefix', value: '=' },
567
- { type: 'string', value: '"true"' },
568
- { type: 'operator', value: '-' },
569
- { type: 'number', value: '1' }
576
+ { type: FX_PREFIX, value: '=' },
577
+ { type: STRING, value: '"true"' },
578
+ { type: OPERATOR, value: '-' },
579
+ { type: NUMBER, value: '1' }
570
580
  ]);
571
581
  t.isTokensNeg('="true" -1', [
572
- { type: 'fx-prefix', value: '=' },
573
- { type: 'string', value: '"true"' },
574
- { type: 'whitespace', value: ' ' },
575
- { type: 'operator', value: '-' },
576
- { type: 'number', value: '1' }
582
+ { type: FX_PREFIX, value: '=' },
583
+ { type: STRING, value: '"true"' },
584
+ { type: WHITESPACE, value: ' ' },
585
+ { type: OPERATOR, value: '-' },
586
+ { type: NUMBER, value: '1' }
577
587
  ]);
578
588
 
579
589
  t.isTokensNeg('=SUM(1)-1', [
@@ -653,7 +663,7 @@ test('tokenize simple equations', t => {
653
663
  ]);
654
664
  t.isTokens(' = ( 1.1+2 ) - 3 ', [
655
665
  { type: WHITESPACE, value: ' ' },
656
- { type: OPERATOR, value: '=' }, // FX_PREFIX
666
+ { type: OPERATOR, value: '=' }, // FX_PREFIX?
657
667
  { type: WHITESPACE, value: ' ' },
658
668
  { type: OPERATOR, value: '(' },
659
669
  { type: WHITESPACE, value: ' ' },
@@ -866,8 +876,7 @@ test('tokenize R1C1 style references', t => {
866
876
 
867
877
  t.isTokens('=[filename]Sheetname!R[-2]C:R[-1]C', [
868
878
  { type: FX_PREFIX, value: '=' },
869
- { type: PATH_BRACE, value: '[filename]' },
870
- { type: PATH_PREFIX, value: 'Sheetname' },
879
+ { type: CONTEXT, value: '[filename]Sheetname' },
871
880
  { type: OPERATOR, value: '!' },
872
881
  { type: RANGE, value: 'R[-2]C' },
873
882
  { type: OPERATOR, value: ':' },
@@ -992,7 +1001,7 @@ test('tokenize A1 style references', t => {
992
1001
 
993
1002
  t.isTokens('=Sheetname!A1', [
994
1003
  { type: FX_PREFIX, value: '=' },
995
- { type: PATH_PREFIX, value: 'Sheetname' },
1004
+ { type: CONTEXT, value: 'Sheetname' },
996
1005
  { type: OPERATOR, value: '!' },
997
1006
  { type: RANGE, value: 'A1' }
998
1007
  ], { mergeRanges: false });
@@ -1004,7 +1013,7 @@ test('tokenize A1 style references', t => {
1004
1013
 
1005
1014
  t.isTokens('=Sheet1!A1:B2', [
1006
1015
  { type: FX_PREFIX, value: '=' },
1007
- { type: PATH_PREFIX, value: 'Sheet1' },
1016
+ { type: CONTEXT, value: 'Sheet1' },
1008
1017
  { type: OPERATOR, value: '!' },
1009
1018
  { type: RANGE, value: 'A1' },
1010
1019
  { type: OPERATOR, value: ':' },
@@ -1018,7 +1027,7 @@ test('tokenize A1 style references', t => {
1018
1027
 
1019
1028
  t.isTokens("='Sheet name'!A1:B2", [
1020
1029
  { type: FX_PREFIX, value: '=' },
1021
- { type: PATH_QUOTE, value: "'Sheet name'" },
1030
+ { type: CONTEXT_QUOTE, value: "'Sheet name'" },
1022
1031
  { type: OPERATOR, value: '!' },
1023
1032
  { type: RANGE, value: 'A1' },
1024
1033
  { type: OPERATOR, value: ':' },
@@ -1032,7 +1041,7 @@ test('tokenize A1 style references', t => {
1032
1041
 
1033
1042
  t.isTokens("='Sheets'' name'!A1:B2", [
1034
1043
  { type: FX_PREFIX, value: '=' },
1035
- { type: PATH_QUOTE, value: "'Sheets'' name'" },
1044
+ { type: CONTEXT_QUOTE, value: "'Sheets'' name'" },
1036
1045
  { type: OPERATOR, value: '!' },
1037
1046
  { type: RANGE, value: 'A1' },
1038
1047
  { type: OPERATOR, value: ':' },
@@ -1046,8 +1055,7 @@ test('tokenize A1 style references', t => {
1046
1055
 
1047
1056
  t.isTokens('=[filename]Sheetname!A1', [
1048
1057
  { type: FX_PREFIX, value: '=' },
1049
- { type: PATH_BRACE, value: '[filename]' },
1050
- { type: PATH_PREFIX, value: 'Sheetname' },
1058
+ { type: CONTEXT, value: '[filename]Sheetname' },
1051
1059
  { type: OPERATOR, value: '!' },
1052
1060
  { type: RANGE, value: 'A1' }
1053
1061
  ], { mergeRanges: false });
@@ -1059,7 +1067,7 @@ test('tokenize A1 style references', t => {
1059
1067
 
1060
1068
  t.isTokens("='[filename]Sheets'' name'!A1:B2", [
1061
1069
  { type: FX_PREFIX, value: '=' },
1062
- { type: PATH_QUOTE, value: "'[filename]Sheets'' name'" },
1070
+ { type: CONTEXT_QUOTE, value: "'[filename]Sheets'' name'" },
1063
1071
  { type: OPERATOR, value: '!' },
1064
1072
  { type: RANGE, value: 'A1' },
1065
1073
  { type: OPERATOR, value: ':' },
@@ -1073,7 +1081,7 @@ test('tokenize A1 style references', t => {
1073
1081
 
1074
1082
  t.isTokens("='Run forest, run!'!A1", [
1075
1083
  { type: FX_PREFIX, value: '=' },
1076
- { type: PATH_QUOTE, value: "'Run forest, run!'" },
1084
+ { type: CONTEXT_QUOTE, value: "'Run forest, run!'" },
1077
1085
  { type: OPERATOR, value: '!' },
1078
1086
  { type: RANGE, value: 'A1' }
1079
1087
  ], { mergeRanges: false });
@@ -1085,7 +1093,7 @@ test('tokenize A1 style references', t => {
1085
1093
 
1086
1094
  t.isTokens("='foo'''!A1", [
1087
1095
  { type: FX_PREFIX, value: '=' },
1088
- { type: PATH_QUOTE, value: "'foo'''" },
1096
+ { type: CONTEXT_QUOTE, value: "'foo'''" },
1089
1097
  { type: OPERATOR, value: '!' },
1090
1098
  { type: RANGE, value: 'A1' }
1091
1099
  ], { mergeRanges: false });
@@ -1097,7 +1105,7 @@ test('tokenize A1 style references', t => {
1097
1105
 
1098
1106
  t.isTokens("='foo'''''!A1", [
1099
1107
  { type: FX_PREFIX, value: '=' },
1100
- { type: PATH_QUOTE, value: "'foo'''''" },
1108
+ { type: CONTEXT_QUOTE, value: "'foo'''''" },
1101
1109
  { type: OPERATOR, value: '!' },
1102
1110
  { type: RANGE, value: 'A1' }
1103
1111
  ], { mergeRanges: false });
@@ -1107,16 +1115,15 @@ test('tokenize A1 style references', t => {
1107
1115
  { type: RANGE, value: '[15]Sheet32!X4' }
1108
1116
  ]);
1109
1117
 
1118
+ // illegal syntax
1110
1119
  t.isTokens('=[15]!named', [
1111
1120
  { type: FX_PREFIX, value: '=' },
1112
- { type: RANGE_NAMED, value: '[15]!named' }
1113
- ]);
1114
- t.isTokens('=[15]!named', [
1115
- { type: FX_PREFIX, value: '=' },
1116
- { type: PATH_BRACE, value: '[15]' },
1121
+ { type: UNKNOWN, value: '[' },
1122
+ { type: NUMBER, value: '15' },
1123
+ { type: UNKNOWN, value: ']' },
1117
1124
  { type: OPERATOR, value: '!' },
1118
1125
  { type: RANGE_NAMED, value: 'named' }
1119
- ], { mergeRanges: false });
1126
+ ]);
1120
1127
 
1121
1128
  t.isTokens('=filename!named', [
1122
1129
  { type: FX_PREFIX, value: '=' },
@@ -1124,15 +1131,14 @@ test('tokenize A1 style references', t => {
1124
1131
  ]);
1125
1132
  t.isTokens('=filename!named', [
1126
1133
  { type: FX_PREFIX, value: '=' },
1127
- { type: PATH_PREFIX, value: 'filename' },
1134
+ { type: CONTEXT, value: 'filename' },
1128
1135
  { type: OPERATOR, value: '!' },
1129
1136
  { type: RANGE_NAMED, value: 'named' }
1130
1137
  ], { mergeRanges: false });
1131
1138
 
1132
1139
  t.isTokens('=[15]Sheet32!X4', [
1133
1140
  { type: FX_PREFIX, value: '=' },
1134
- { type: PATH_BRACE, value: '[15]' },
1135
- { type: PATH_PREFIX, value: 'Sheet32' },
1141
+ { type: CONTEXT, value: '[15]Sheet32' },
1136
1142
  { type: OPERATOR, value: '!' },
1137
1143
  { type: RANGE, value: 'X4' }
1138
1144
  ], { mergeRanges: false });
@@ -1158,7 +1164,7 @@ test('tokenize A1 style references', t => {
1158
1164
  //
1159
1165
  t.isTokens("='D:\\Reports\\Sales.xlsx'!namedrange", [
1160
1166
  { type: FX_PREFIX, value: '=' },
1161
- { type: PATH_QUOTE, value: "'D:\\Reports\\Sales.xlsx'" },
1167
+ { type: CONTEXT_QUOTE, value: "'D:\\Reports\\Sales.xlsx'" },
1162
1168
  { type: OPERATOR, value: '!' },
1163
1169
  { type: RANGE_NAMED, value: 'namedrange' }
1164
1170
  ], { mergeRanges: false });
@@ -1169,7 +1175,7 @@ test('tokenize A1 style references', t => {
1169
1175
 
1170
1176
  t.isTokens('=Sales.xlsx!namedrange', [
1171
1177
  { type: FX_PREFIX, value: '=' },
1172
- { type: PATH_PREFIX, value: 'Sales.xlsx' },
1178
+ { type: CONTEXT, value: 'Sales.xlsx' },
1173
1179
  { type: OPERATOR, value: '!' },
1174
1180
  { type: RANGE_NAMED, value: 'namedrange' }
1175
1181
  ], { mergeRanges: false });
@@ -1184,7 +1190,7 @@ test('tokenize A1 style references', t => {
1184
1190
  ]);
1185
1191
  t.isTokens('=Sheet1!A:A', [
1186
1192
  { type: FX_PREFIX, value: '=' },
1187
- { type: PATH_PREFIX, value: 'Sheet1' },
1193
+ { type: CONTEXT, value: 'Sheet1' },
1188
1194
  { type: OPERATOR, value: '!' },
1189
1195
  { type: RANGE_BEAM, value: 'A:A' }
1190
1196
  ], { mergeRanges: false });
@@ -1197,7 +1203,7 @@ test('tokenize A1 style references', t => {
1197
1203
  ]);
1198
1204
  t.isTokens('=Sheet1!A:A:B:B', [
1199
1205
  { type: FX_PREFIX, value: '=' },
1200
- { type: PATH_PREFIX, value: 'Sheet1' },
1206
+ { type: CONTEXT, value: 'Sheet1' },
1201
1207
  { type: OPERATOR, value: '!' },
1202
1208
  { type: RANGE_BEAM, value: 'A:A' },
1203
1209
  { type: OPERATOR, value: ':' },
@@ -1269,7 +1275,7 @@ test('tokenize errors', t => {
1269
1275
  t.isTokens('=#NONSENSE!', [
1270
1276
  { type: FX_PREFIX, value: '=' },
1271
1277
  { type: OPERATOR, value: '#' },
1272
- { type: PATH_PREFIX, value: 'NONSENSE' },
1278
+ { type: CONTEXT, value: 'NONSENSE' },
1273
1279
  { type: OPERATOR, value: '!' }
1274
1280
  ]);
1275
1281
 
@@ -1338,26 +1344,323 @@ test('tokenize strings', t => {
1338
1344
  // we should be able to highlight things that are still being typed
1339
1345
  // so open-ended STRING, PATH_BRACE, and PATH_QUOTE tokens at the
1340
1346
  // end of output are tagged.
1347
+ t.isTokens('="incomple', [
1348
+ { type: FX_PREFIX, value: '=' },
1349
+ { type: STRING, value: '"incomple', unterminated: true }
1350
+ ]);
1351
+
1341
1352
  t.isTokens('="', [
1342
1353
  { type: FX_PREFIX, value: '=' },
1343
1354
  { type: STRING, value: '"', unterminated: true }
1344
1355
  ]);
1345
- t.isTokens('="incomple', [
1356
+ t.isTokens('=""', [
1346
1357
  { type: FX_PREFIX, value: '=' },
1347
- { type: STRING, value: '"incomple', unterminated: true }
1358
+ { type: STRING, value: '""' }
1359
+ ]);
1360
+ t.isTokens('="""', [
1361
+ { type: FX_PREFIX, value: '=' },
1362
+ { type: STRING, value: '"""', unterminated: true }
1363
+ ]);
1364
+ t.isTokens('=""""', [
1365
+ { type: FX_PREFIX, value: '=' },
1366
+ { type: STRING, value: '""""' }
1367
+ ]);
1368
+ t.isTokens('="""""', [
1369
+ { type: FX_PREFIX, value: '=' },
1370
+ { type: STRING, value: '"""""', unterminated: true }
1371
+ ]);
1372
+ t.isTokens('=""""""', [
1373
+ { type: FX_PREFIX, value: '=' },
1374
+ { type: STRING, value: '""""""' }
1375
+ ]);
1376
+ t.isTokens('="aa""ss', [
1377
+ { type: FX_PREFIX, value: '=' },
1378
+ { type: STRING, value: '"aa""ss', unterminated: true }
1379
+ ]);
1380
+ t.isTokens('="aa""ss"', [
1381
+ { type: FX_PREFIX, value: '=' },
1382
+ { type: STRING, value: '"aa""ss"' }
1383
+ ]);
1384
+ t.isTokens('="aa""', [
1385
+ { type: FX_PREFIX, value: '=' },
1386
+ { type: STRING, value: '"aa""', unterminated: true }
1387
+ ]);
1388
+ t.isTokens('="aa"""', [
1389
+ { type: FX_PREFIX, value: '=' },
1390
+ { type: STRING, value: '"aa"""' }
1391
+ ]);
1392
+
1393
+ t.end();
1394
+ });
1395
+
1396
+ test('unknown and unary minus mess with offsets', t => {
1397
+ t.isTokens('=-1', [
1398
+ { type: FX_PREFIX, value: '=', range: [ 0, 1 ] },
1399
+ { type: OPERATOR, value: '-', range: [ 1, 2 ] },
1400
+ { type: NUMBER, value: '1', range: [ 2, 3 ] }
1401
+ ], { emitRanges: true });
1402
+ t.isTokens('=-1', [
1403
+ { type: FX_PREFIX, value: '=', range: [ 0, 1 ] },
1404
+ { type: NUMBER, value: '-1', range: [ 1, 3 ] }
1405
+ ], { emitRanges: true, negativeNumbers: true });
1406
+
1407
+ t.isTokens('=$C', [
1408
+ { type: FX_PREFIX, value: '=', range: [ 0, 1 ] },
1409
+ { type: UNKNOWN, value: '$C', range: [ 1, 3 ] }
1410
+ ], { emitRanges: true });
1411
+ t.isTokens('=$C.foo', [
1412
+ { type: FX_PREFIX, value: '=', range: [ 0, 1 ] },
1413
+ { type: UNKNOWN, value: '$C.foo', range: [ 1, 7 ] }
1414
+ ], { emitRanges: true });
1415
+
1416
+ t.end();
1417
+ });
1418
+
1419
+ test('unknowns, named ranges and functions', t => {
1420
+ t.isTokens('=foo', [
1421
+ { type: FX_PREFIX, value: '=' },
1422
+ { type: RANGE_NAMED, value: 'foo' }
1423
+ ]);
1424
+ t.isTokens('=_foo', [
1425
+ { type: FX_PREFIX, value: '=' },
1426
+ { type: RANGE_NAMED, value: '_foo' }
1427
+ ]);
1428
+ t.isTokens('=\\foo', [
1429
+ { type: FX_PREFIX, value: '=' },
1430
+ { type: RANGE_NAMED, value: '\\foo' }
1431
+ ]);
1432
+ t.isTokens('=æði', [
1433
+ { type: FX_PREFIX, value: '=' },
1434
+ { type: RANGE_NAMED, value: 'æði' }
1435
+ ]);
1436
+ t.isTokens('=らーめん', [
1437
+ { type: FX_PREFIX, value: '=' },
1438
+ { type: RANGE_NAMED, value: 'らーめん' }
1439
+ ]);
1440
+ t.isTokens('=@foo', [
1441
+ { type: FX_PREFIX, value: '=' },
1442
+ { type: OPERATOR, value: '@' },
1443
+ { type: RANGE_NAMED, value: 'foo' }
1444
+ ]);
1445
+ t.isTokens('=9æði', [
1446
+ { type: FX_PREFIX, value: '=' },
1447
+ { type: NUMBER, value: '9' },
1448
+ { type: RANGE_NAMED, value: 'æði' }
1449
+ ]);
1450
+ t.isTokens('=¢mah¢', [
1451
+ { type: FX_PREFIX, value: '=' },
1452
+ { type: RANGE_NAMED, value: '¢mah¢' }
1453
+ ]);
1454
+ t.isTokens('=~mah~', [
1455
+ { type: FX_PREFIX, value: '=' },
1456
+ { type: UNKNOWN, value: '~mah~' }
1348
1457
  ]);
1349
- t.isTokens('=[Foo', [
1458
+ t.isTokens('=$foo', [
1350
1459
  { type: FX_PREFIX, value: '=' },
1351
- { type: PATH_BRACE, value: '[Foo', unterminated: true }
1460
+ { type: UNKNOWN, value: '$foo' }
1352
1461
  ]);
1353
- t.isTokens("='", [
1462
+ t.isTokens('=$zzzz12', [
1354
1463
  { type: FX_PREFIX, value: '=' },
1355
- { type: PATH_QUOTE, value: "'", unterminated: true }
1464
+ { type: UNKNOWN, value: '$zzzz12' }
1356
1465
  ]);
1357
- t.isTokens("='Sheet name", [
1466
+ t.isTokens('=~zzzz12()', [
1358
1467
  { type: FX_PREFIX, value: '=' },
1359
- { type: PATH_QUOTE, value: "'Sheet name", unterminated: true }
1468
+ { type: UNKNOWN, value: '~zzzz12' },
1469
+ { type: OPERATOR, value: '(' },
1470
+ { type: OPERATOR, value: ')' }
1471
+ ]);
1472
+ t.isTokens('=zzzz~12()', [
1473
+ { type: FX_PREFIX, value: '=' },
1474
+ { type: UNKNOWN, value: 'zzzz~' },
1475
+ { type: NUMBER, value: '12' },
1476
+ { type: OPERATOR, value: '(' },
1477
+ { type: OPERATOR, value: ')' }
1360
1478
  ]);
1479
+ t.end();
1480
+ });
1481
+
1482
+ test('tokenize partial ranges', t => {
1483
+ const opts = { allowTernary: true };
1484
+ t.isTokens('1:D$1', [
1485
+ { type: RANGE_TERNARY, value: '1:D$1' }
1486
+ ], opts);
1487
+
1488
+ t.isTokens('1:D$1', [
1489
+ { type: NUMBER, value: '1' },
1490
+ { type: OPERATOR, value: ':' },
1491
+ { type: RANGE, value: 'D$1' }
1492
+ ]);
1493
+
1494
+ t.isTokens('B2:B', [
1495
+ { type: RANGE_TERNARY, value: 'B2:B' }
1496
+ ], opts);
1497
+ t.isTokens('B2:B', [
1498
+ { type: RANGE, value: 'B2' },
1499
+ { type: OPERATOR, value: ':' },
1500
+ { type: RANGE_NAMED, value: 'B' }
1501
+ ]);
1502
+
1503
+ // form 1
1504
+ t.isTokens('1:A1', [
1505
+ { type: RANGE_TERNARY, value: '1:A1' }
1506
+ ], opts);
1507
+ t.isTokens('$1:A1', [
1508
+ { type: RANGE_TERNARY, value: '$1:A1' }
1509
+ ], opts);
1510
+ t.isTokens('1:$A1', [
1511
+ { type: RANGE_TERNARY, value: '1:$A1' }
1512
+ ], opts);
1513
+ t.isTokens('1:A$1', [
1514
+ { type: RANGE_TERNARY, value: '1:A$1' }
1515
+ ], opts);
1516
+ t.isTokens('1:$A$1', [
1517
+ { type: RANGE_TERNARY, value: '1:$A$1' }
1518
+ ], opts);
1519
+ t.isTokens('$1:A$1', [
1520
+ { type: RANGE_TERNARY, value: '$1:A$1' }
1521
+ ], opts);
1522
+ t.isTokens('$1:$A1', [
1523
+ { type: RANGE_TERNARY, value: '$1:$A1' }
1524
+ ], opts);
1525
+ t.isTokens('$1:$A$1', [
1526
+ { type: RANGE_TERNARY, value: '$1:$A$1' }
1527
+ ], opts);
1528
+
1529
+ // form 2
1530
+ t.isTokens('A1:1', [
1531
+ { type: RANGE_TERNARY, value: 'A1:1' }
1532
+ ], opts);
1533
+ t.isTokens('A1:$1', [
1534
+ { type: RANGE_TERNARY, value: 'A1:$1' }
1535
+ ], opts);
1536
+ t.isTokens('$A1:1', [
1537
+ { type: RANGE_TERNARY, value: '$A1:1' }
1538
+ ], opts);
1539
+ t.isTokens('A$1:1', [
1540
+ { type: RANGE_TERNARY, value: 'A$1:1' }
1541
+ ], opts);
1542
+ t.isTokens('$A$1:1', [
1543
+ { type: RANGE_TERNARY, value: '$A$1:1' }
1544
+ ], opts);
1545
+ t.isTokens('A$1:$1', [
1546
+ { type: RANGE_TERNARY, value: 'A$1:$1' }
1547
+ ], opts);
1548
+ t.isTokens('$A1:$1', [
1549
+ { type: RANGE_TERNARY, value: '$A1:$1' }
1550
+ ], opts);
1551
+ t.isTokens('$A$1:$1', [
1552
+ { type: RANGE_TERNARY, value: '$A$1:$1' }
1553
+ ], opts);
1554
+
1555
+ // form 3
1556
+ t.isTokens('A:A1', [
1557
+ { type: RANGE_TERNARY, value: 'A:A1' }
1558
+ ], opts);
1559
+ t.isTokens('$A:A1', [
1560
+ { type: RANGE_TERNARY, value: '$A:A1' }
1561
+ ], opts);
1562
+ t.isTokens('A:$A1', [
1563
+ { type: RANGE_TERNARY, value: 'A:$A1' }
1564
+ ], opts);
1565
+ t.isTokens('A:A$1', [
1566
+ { type: RANGE_TERNARY, value: 'A:A$1' }
1567
+ ], opts);
1568
+ t.isTokens('A:$A$1', [
1569
+ { type: RANGE_TERNARY, value: 'A:$A$1' }
1570
+ ], opts);
1571
+ t.isTokens('$A:A$1', [
1572
+ { type: RANGE_TERNARY, value: '$A:A$1' }
1573
+ ], opts);
1574
+ t.isTokens('$A:$A1', [
1575
+ { type: RANGE_TERNARY, value: '$A:$A1' }
1576
+ ], opts);
1577
+ t.isTokens('$A:$A$1', [
1578
+ { type: RANGE_TERNARY, value: '$A:$A$1' }
1579
+ ], opts);
1580
+
1581
+ // form 4
1582
+ t.isTokens('A1:A', [
1583
+ { type: RANGE_TERNARY, value: 'A1:A' }
1584
+ ], opts);
1585
+ t.isTokens('A1:$A', [
1586
+ { type: RANGE_TERNARY, value: 'A1:$A' }
1587
+ ], opts);
1588
+ t.isTokens('$A1:A', [
1589
+ { type: RANGE_TERNARY, value: '$A1:A' }
1590
+ ], opts);
1591
+ t.isTokens('A$1:A', [
1592
+ { type: RANGE_TERNARY, value: 'A$1:A' }
1593
+ ], opts);
1594
+ t.isTokens('$A$1:A', [
1595
+ { type: RANGE_TERNARY, value: '$A$1:A' }
1596
+ ], opts);
1597
+ t.isTokens('A$1:$A', [
1598
+ { type: RANGE_TERNARY, value: 'A$1:$A' }
1599
+ ], opts);
1600
+ t.isTokens('$A1:$A', [
1601
+ { type: RANGE_TERNARY, value: '$A1:$A' }
1602
+ ], opts);
1603
+ t.isTokens('$A$1:$A', [
1604
+ { type: RANGE_TERNARY, value: '$A$1:$A' }
1605
+ ], opts);
1606
+
1607
+ t.isTokens('=A10:A+B1:2', [
1608
+ { type: FX_PREFIX, value: '=' },
1609
+ { type: RANGE_TERNARY, value: 'A10:A' },
1610
+ { type: OPERATOR, value: '+' },
1611
+ { type: RANGE_TERNARY, value: 'B1:2' }
1612
+ ], opts);
1613
+ t.isTokens('=SUM(A:A$10,3:B$2)', [
1614
+ { type: FX_PREFIX, value: '=' },
1615
+ { type: FUNCTION, value: 'SUM' },
1616
+ { type: OPERATOR, value: '(' },
1617
+ { type: RANGE_TERNARY, value: 'A:A$10' },
1618
+ { type: OPERATOR, value: ',' },
1619
+ { type: RANGE_TERNARY, value: '3:B$2' },
1620
+ { type: OPERATOR, value: ')' }
1621
+ ], opts);
1622
+ t.isTokens('$A$10:$12', [
1623
+ { type: RANGE_TERNARY, value: '$A$10:$12' }
1624
+ ], opts);
1625
+ t.isTokens('1:D$1', [
1626
+ { type: RANGE_TERNARY, value: '1:D$1' }
1627
+ ], opts);
1628
+ t.isTokens('=A1:IF()', [
1629
+ { type: FX_PREFIX, value: '=' },
1630
+ { type: RANGE, value: 'A1' },
1631
+ { type: OPERATOR, value: ':' },
1632
+ { type: FUNCTION, value: 'IF' },
1633
+ { type: OPERATOR, value: '(' },
1634
+ { type: OPERATOR, value: ')' }
1635
+ ], opts);
1636
+ t.isTokens('=A1:F.DIST()', [
1637
+ { type: FX_PREFIX, value: '=' },
1638
+ { type: RANGE, value: 'A1' },
1639
+ { type: OPERATOR, value: ':' },
1640
+ { type: FUNCTION, value: 'F.DIST' },
1641
+ { type: OPERATOR, value: '(' },
1642
+ { type: OPERATOR, value: ')' }
1643
+ ], opts);
1644
+ t.isTokens('=1:A1.', [
1645
+ { type: FX_PREFIX, value: '=' },
1646
+ { type: NUMBER, value: '1' },
1647
+ { type: OPERATOR, value: ':' },
1648
+ { type: RANGE, value: 'A1' },
1649
+ { type: UNKNOWN, value: '.' }
1650
+ ], opts);
1651
+ t.isTokens('=A1:X$', [
1652
+ { type: FX_PREFIX, value: '=' },
1653
+ { type: RANGE, value: 'A1' },
1654
+ { type: OPERATOR, value: ':' },
1655
+ { type: UNKNOWN, value: 'X$' }
1656
+ ], opts);
1657
+
1658
+ t.isTokens('=[foo]Bar!A:A1', [
1659
+ { type: FX_PREFIX, value: '=' },
1660
+ { type: CONTEXT, value: '[foo]Bar' },
1661
+ { type: OPERATOR, value: '!' },
1662
+ { type: RANGE_TERNARY, value: 'A:A1' }
1663
+ ], { mergeRanges: false, allowTernary: true });
1361
1664
 
1362
1665
  t.end();
1363
1666
  });