1import { parse, match, interpolation } from './core';
2
3const expectToParse = (node, input, result, lastIndex = 0) => {
4 const state = { quasis: [input], expressions: [], x: 0, y: 0 };
5 if (result) result.tag = 'node';
6 expect(node(state)).toEqual(result);
7
8 // NOTE: After parsing we expect the current index to exactly match the
9 // sum amount of matched characters
10 if (result === undefined) {
11 expect(state.y).toBe(0);
12 } else {
13 const index = lastIndex || result.reduce((acc, x) => acc + x.length, 0);
14 expect(state.y).toBe(index);
15 }
16};
17
18describe('can create nameless matchers', () => {
19 it('matches without tagging', () => {
20 const state = { quasis: ['1'], expressions: [], x: 0, y: 0 };
21 const node = match(null)`${/1/}`;
22 expect(node(state)).toEqual(['1']);
23 });
24});
25
26describe('required matcher', () => {
27 const node = match('node')`${/1/}`;
28 it.each`
29 input | result
30 ${'1'} | ${['1']}
31 ${''} | ${undefined}
32 `('should return $result when $input is passed', ({ input, result }) => {
33 expectToParse(node, input, result);
34 });
35
36 it('matches empty regex patterns', () => {
37 const node = match('node')`${/[ ]*/}`;
38 expectToParse(node, '', ['']);
39 });
40});
41
42describe('optional matcher', () => {
43 const node = match('node')`${/1/}?`;
44 it.each`
45 input | result
46 ${'1'} | ${['1']}
47 ${'_'} | ${[]}
48 ${''} | ${[]}
49 `('should return $result when $input is passed', ({ input, result }) => {
50 expectToParse(node, input, result);
51 });
52});
53
54describe('star matcher', () => {
55 const node = match('node')`${/1/}*`;
56 it.each`
57 input | result
58 ${'1'} | ${['1']}
59 ${'11'} | ${['1', '1']}
60 ${'111'} | ${['1', '1', '1']}
61 ${'_'} | ${[]}
62 ${''} | ${[]}
63 `('should return $result when "$input" is passed', ({ input, result }) => {
64 expectToParse(node, input, result);
65 });
66});
67
68describe('plus matcher', () => {
69 const node = match('node')`${/1/}+`;
70 it.each`
71 input | result
72 ${'1'} | ${['1']}
73 ${'11'} | ${['1', '1']}
74 ${'111'} | ${['1', '1', '1']}
75 ${'_'} | ${undefined}
76 ${''} | ${undefined}
77 `('should return $result when "$input" is passed', ({ input, result }) => {
78 expectToParse(node, input, result);
79 });
80});
81
82describe('optional then required matcher', () => {
83 const node = match('node')`${/1/}? ${/2/}`;
84 it.each`
85 input | result
86 ${'12'} | ${['1', '2']}
87 ${'2'} | ${['2']}
88 ${''} | ${undefined}
89 `('should return $result when $input is passed', ({ input, result }) => {
90 expectToParse(node, input, result);
91 });
92});
93
94describe('star then required matcher', () => {
95 const node = match('node')`${/1/}* ${/2/}`;
96 it.each`
97 input | result
98 ${'12'} | ${['1', '2']}
99 ${'112'} | ${['1', '1', '2']}
100 ${'2'} | ${['2']}
101 ${''} | ${undefined}
102 `('should return $result when $input is passed', ({ input, result }) => {
103 expectToParse(node, input, result);
104 });
105});
106
107describe('plus then required matcher', () => {
108 const node = match('node')`${/1/}+ ${/2/}`;
109 it.each`
110 input | result
111 ${'12'} | ${['1', '2']}
112 ${'112'} | ${['1', '1', '2']}
113 ${'2'} | ${undefined}
114 ${''} | ${undefined}
115 `('should return $result when $input is passed', ({ input, result }) => {
116 expectToParse(node, input, result);
117 });
118});
119
120describe('optional group then required matcher', () => {
121 const node = match('node')`(${/1/} ${/2/})? ${/3/}`;
122 it.each`
123 input | result
124 ${'123'} | ${['1', '2', '3']}
125 ${'3'} | ${['3']}
126 ${'23'} | ${undefined}
127 ${'_'} | ${undefined}
128 `('should return $result when $input is passed', ({ input, result }) => {
129 expectToParse(node, input, result);
130 });
131});
132
133describe('star group then required matcher', () => {
134 const node = match('node')`(${/1/} ${/2/})* ${/3/}`;
135 it.each`
136 input | result
137 ${'123'} | ${['1', '2', '3']}
138 ${'12123'} | ${['1', '2', '1', '2', '3']}
139 ${'3'} | ${['3']}
140 ${'23'} | ${undefined}
141 ${'13'} | ${undefined}
142 ${'_'} | ${undefined}
143 `('should return $result when $input is passed', ({ input, result }) => {
144 expectToParse(node, input, result);
145 });
146});
147
148describe('plus group then required matcher', () => {
149 const node = match('node')`(${/1/} ${/2/})+ ${/3/}`;
150 it.each`
151 input | result
152 ${'123'} | ${['1', '2', '3']}
153 ${'12123'} | ${['1', '2', '1', '2', '3']}
154 ${'23'} | ${undefined}
155 ${'3'} | ${undefined}
156 ${'13'} | ${undefined}
157 ${'_'} | ${undefined}
158 `('should return $result when $input is passed', ({ input, result }) => {
159 expectToParse(node, input, result);
160 });
161});
162
163describe('optional group with nested optional matcher, then required matcher', () => {
164 const node = match('node')`(${/1/}? ${/2/})? ${/3/}`;
165 it.each`
166 input | result
167 ${'123'} | ${['1', '2', '3']}
168 ${'23'} | ${['2', '3']}
169 ${'3'} | ${['3']}
170 ${'13'} | ${undefined}
171 ${'_'} | ${undefined}
172 `('should return $result when $input is passed', ({ input, result }) => {
173 expectToParse(node, input, result);
174 });
175});
176
177describe('star group with nested optional matcher, then required matcher', () => {
178 const node = match('node')`(${/1/}? ${/2/})* ${/3/}`;
179 it.each`
180 input | result
181 ${'123'} | ${['1', '2', '3']}
182 ${'23'} | ${['2', '3']}
183 ${'223'} | ${['2', '2', '3']}
184 ${'2123'} | ${['2', '1', '2', '3']}
185 ${'3'} | ${['3']}
186 ${'13'} | ${undefined}
187 ${'_'} | ${undefined}
188 `('should return $result when $input is passed', ({ input, result }) => {
189 expectToParse(node, input, result);
190 });
191});
192
193describe('plus group with nested optional matcher, then required matcher', () => {
194 const node = match('node')`(${/1/}? ${/2/})+ ${/3/}`;
195 it.each`
196 input | result
197 ${'123'} | ${['1', '2', '3']}
198 ${'23'} | ${['2', '3']}
199 ${'223'} | ${['2', '2', '3']}
200 ${'2123'} | ${['2', '1', '2', '3']}
201 ${'3'} | ${undefined}
202 ${'13'} | ${undefined}
203 ${'_'} | ${undefined}
204 `('should return $result when $input is passed', ({ input, result }) => {
205 expectToParse(node, input, result);
206 });
207});
208
209describe('plus group with nested plus matcher, then required matcher', () => {
210 const node = match('node')`(${/1/}+ ${/2/})+ ${/3/}`;
211 it.each`
212 input | result
213 ${'123'} | ${['1', '2', '3']}
214 ${'1123'} | ${['1', '1', '2', '3']}
215 ${'12123'} | ${['1', '2', '1', '2', '3']}
216 ${'121123'} | ${['1', '2', '1', '1', '2', '3']}
217 ${'3'} | ${undefined}
218 ${'23'} | ${undefined}
219 ${'13'} | ${undefined}
220 ${'_'} | ${undefined}
221 `('should return $result when $input is passed', ({ input, result }) => {
222 expectToParse(node, input, result);
223 });
224});
225
226describe('plus group with nested required and plus matcher, then required matcher', () => {
227 const node = match('node')`(${/1/} ${/2/}+)+ ${/3/}`;
228 it.each`
229 input | result
230 ${'123'} | ${['1', '2', '3']}
231 ${'1223'} | ${['1', '2', '2', '3']}
232 ${'122123'} | ${['1', '2', '2', '1', '2', '3']}
233 ${'13'} | ${undefined}
234 ${'_'} | ${undefined}
235 `('should return $result when $input is passed', ({ input, result }) => {
236 expectToParse(node, input, result);
237 });
238});
239
240describe('nested plus group with nested required and plus matcher, then required matcher or alternate', () => {
241 const node = match('node')`(${/1/} ${/2/}+)+ ${/3/} | ${/1/}`;
242 it.each`
243 input | result
244 ${'123'} | ${['1', '2', '3']}
245 ${'1223'} | ${['1', '2', '2', '3']}
246 ${'122123'} | ${['1', '2', '2', '1', '2', '3']}
247 ${'1'} | ${['1']}
248 ${'13'} | ${['1']}
249 ${'_'} | ${undefined}
250 `('should return $result when $input is passed', ({ input, result }) => {
251 expectToParse(node, input, result);
252 });
253});
254
255describe('nested plus group with nested required and plus matcher, then alternate', () => {
256 const node = match('node')`(${/1/} ${/2/}+)+ (${/3/} | ${/4/})`;
257 it.each`
258 input | result
259 ${'123'} | ${['1', '2', '3']}
260 ${'124'} | ${['1', '2', '4']}
261 ${'1223'} | ${['1', '2', '2', '3']}
262 ${'1224'} | ${['1', '2', '2', '4']}
263 ${'1'} | ${undefined}
264 ${'13'} | ${undefined}
265 ${'_'} | ${undefined}
266 `('should return $result when $input is passed', ({ input, result }) => {
267 expectToParse(node, input, result);
268 });
269});
270
271describe('regular alternate', () => {
272 const node = match('node')`${/1/} | ${/2/} | ${/3/} | ${/4/}`;
273 it.each`
274 input | result
275 ${'1'} | ${['1']}
276 ${'2'} | ${['2']}
277 ${'3'} | ${['3']}
278 ${'4'} | ${['4']}
279 ${'_'} | ${undefined}
280 `('should return $result when $input is passed', ({ input, result }) => {
281 expectToParse(node, input, result);
282 });
283});
284
285describe('nested alternate in nested alternate in alternate', () => {
286 const node = match('node')`((${/1/} | ${/2/}) | ${/3/}) | ${/4/}`;
287 it.each`
288 input | result
289 ${'1'} | ${['1']}
290 ${'2'} | ${['2']}
291 ${'3'} | ${['3']}
292 ${'4'} | ${['4']}
293 ${'_'} | ${undefined}
294 `('should return $result when $input is passed', ({ input, result }) => {
295 expectToParse(node, input, result);
296 });
297});
298
299describe('alternate after required matcher', () => {
300 const node = match('node')`${/1/} (${/2/} | ${/3/})`;
301 it.each`
302 input | result
303 ${'12'} | ${['1', '2']}
304 ${'13'} | ${['1', '3']}
305 ${'14'} | ${undefined}
306 ${'3'} | ${undefined}
307 ${'_'} | ${undefined}
308 `('should return $result when $input is passed', ({ input, result }) => {
309 expectToParse(node, input, result);
310 });
311});
312
313describe('alternate with star group and required matcher after required matcher', () => {
314 const node = match('node')`${/1/} (${/2/}* ${/3/} | ${/4/})`;
315 it.each`
316 input | result
317 ${'123'} | ${['1', '2', '3']}
318 ${'1223'} | ${['1', '2', '2', '3']}
319 ${'13'} | ${['1', '3']}
320 ${'14'} | ${['1', '4']}
321 ${'12'} | ${undefined}
322 ${'15'} | ${undefined}
323 ${'_'} | ${undefined}
324 `('should return $result when $input is passed', ({ input, result }) => {
325 expectToParse(node, input, result);
326 });
327});
328
329describe('alternate with plus group and required matcher after required matcher', () => {
330 const node = match('node')`${/1/} (${/2/}+ ${/3/} | ${/4/})`;
331 it.each`
332 input | result
333 ${'123'} | ${['1', '2', '3']}
334 ${'1223'} | ${['1', '2', '2', '3']}
335 ${'14'} | ${['1', '4']}
336 ${'13'} | ${undefined}
337 ${'12'} | ${undefined}
338 ${'15'} | ${undefined}
339 ${'_'} | ${undefined}
340 `('should return $result when $input is passed', ({ input, result }) => {
341 expectToParse(node, input, result);
342 });
343});
344
345describe('alternate with optional and required matcher after required matcher', () => {
346 const node = match('node')`${/1/} (${/2/}? ${/3/} | ${/4/})`;
347 it.each`
348 input | result
349 ${'123'} | ${['1', '2', '3']}
350 ${'13'} | ${['1', '3']}
351 ${'14'} | ${['1', '4']}
352 ${'12'} | ${undefined}
353 ${'15'} | ${undefined}
354 ${'_'} | ${undefined}
355 `('should return $result when $input is passed', ({ input, result }) => {
356 expectToParse(node, input, result);
357 });
358});
359
360describe('non-capturing group', () => {
361 const node = match('node')`${/1/} (?: ${/2/}+)`;
362 it.each`
363 input | result | lastIndex
364 ${'12'} | ${['1']} | ${2}
365 ${'122'} | ${['1']} | ${3}
366 ${'13'} | ${undefined} | ${0}
367 ${'1'} | ${undefined} | ${0}
368 ${'_'} | ${undefined} | ${0}
369 `(
370 'should return $result when $input is passed',
371 ({ input, result, lastIndex }) => {
372 expectToParse(node, input, result, lastIndex);
373 }
374 );
375});
376
377describe('non-capturing shorthand', () => {
378 const node = match('node')`${/1/} :${/2/}+`;
379 it.each`
380 input | result | lastIndex
381 ${'12'} | ${['1']} | ${2}
382 ${'122'} | ${['1']} | ${3}
383 ${'13'} | ${undefined} | ${0}
384 ${'1'} | ${undefined} | ${0}
385 ${'_'} | ${undefined} | ${0}
386 `(
387 'should return $result when $input is passed',
388 ({ input, result, lastIndex }) => {
389 expectToParse(node, input, result, lastIndex);
390 }
391 );
392});
393
394describe('non-capturing group with plus matcher, then required matcher', () => {
395 const node = match('node')`(?: ${/1/}+) ${/2/}`;
396 it.each`
397 input | result | lastIndex
398 ${'12'} | ${['2']} | ${2}
399 ${'112'} | ${['2']} | ${3}
400 ${'1'} | ${undefined} | ${0}
401 ${'13'} | ${undefined} | ${0}
402 ${'2'} | ${undefined} | ${0}
403 ${'_'} | ${undefined} | ${0}
404 `(
405 'should return $result when $input is passed',
406 ({ input, result, lastIndex }) => {
407 expectToParse(node, input, result, lastIndex);
408 }
409 );
410});
411
412describe('non-capturing group with star group and required matcher, then required matcher', () => {
413 const node = match('node')`(?: ${/1/}* ${/2/}) ${/3/}`;
414 it.each`
415 input | result | lastIndex
416 ${'123'} | ${['3']} | ${3}
417 ${'1123'} | ${['3']} | ${4}
418 ${'23'} | ${['3']} | ${2}
419 ${'13'} | ${undefined} | ${0}
420 ${'2'} | ${undefined} | ${0}
421 ${'_'} | ${undefined} | ${0}
422 `(
423 'should return $result when $input is passed',
424 ({ input, result, lastIndex }) => {
425 expectToParse(node, input, result, lastIndex);
426 }
427 );
428});
429
430describe('non-capturing group with plus group and required matcher, then required matcher', () => {
431 const node = match('node')`(?: ${/1/}+ ${/2/}) ${/3/}`;
432 it.each`
433 input | result | lastIndex
434 ${'123'} | ${['3']} | ${3}
435 ${'1123'} | ${['3']} | ${4}
436 ${'23'} | ${undefined} | ${0}
437 ${'13'} | ${undefined} | ${0}
438 ${'2'} | ${undefined} | ${0}
439 ${'_'} | ${undefined} | ${0}
440 `(
441 'should return $result when $input is passed',
442 ({ input, result, lastIndex }) => {
443 expectToParse(node, input, result, lastIndex);
444 }
445 );
446});
447
448describe('non-capturing group with optional and required matcher, then required matcher', () => {
449 const node = match('node')`(?: ${/1/}? ${/2/}) ${/3/}`;
450 it.each`
451 input | result | lastIndex
452 ${'123'} | ${['3']} | ${3}
453 ${'23'} | ${['3']} | ${2}
454 ${'13'} | ${undefined} | ${0}
455 ${'2'} | ${undefined} | ${0}
456 ${'_'} | ${undefined} | ${0}
457 `(
458 'should return $result when $input is passed',
459 ({ input, result, lastIndex }) => {
460 expectToParse(node, input, result, lastIndex);
461 }
462 );
463});
464
465describe('positive lookahead group', () => {
466 const node = match('node')`(?= ${/1/}) ${/\d/}`;
467 it.each`
468 input | result | lastIndex
469 ${'1'} | ${['1']} | ${1}
470 ${'13'} | ${['1']} | ${1}
471 ${'2'} | ${undefined} | ${0}
472 ${'_'} | ${undefined} | ${0}
473 `(
474 'should return $result when $input is passed',
475 ({ input, result, lastIndex }) => {
476 expectToParse(node, input, result, lastIndex);
477 }
478 );
479});
480
481describe('positive lookahead shorthand', () => {
482 const node = match('node')`=${/1/} ${/\d/}`;
483 it.each`
484 input | result | lastIndex
485 ${'1'} | ${['1']} | ${1}
486 ${'13'} | ${['1']} | ${1}
487 ${'2'} | ${undefined} | ${0}
488 ${'_'} | ${undefined} | ${0}
489 `(
490 'should return $result when $input is passed',
491 ({ input, result, lastIndex }) => {
492 expectToParse(node, input, result, lastIndex);
493 }
494 );
495});
496
497describe('positive lookahead group with plus matcher', () => {
498 const node = match('node')`(?= ${/1/}+) ${/\d/}`;
499 it.each`
500 input | result | lastIndex
501 ${'1'} | ${['1']} | ${1}
502 ${'11'} | ${['1']} | ${1}
503 ${'12'} | ${['1']} | ${1}
504 ${'22'} | ${undefined} | ${0}
505 ${'2'} | ${undefined} | ${0}
506 ${'_'} | ${undefined} | ${0}
507 `(
508 'should return $result when $input is passed',
509 ({ input, result, lastIndex }) => {
510 expectToParse(node, input, result, lastIndex);
511 }
512 );
513});
514
515describe('positive lookahead group with plus group and required matcher', () => {
516 const node = match('node')`(?= ${/1/}+ ${/2/}) ${/\d/}`;
517 it.each`
518 input | result | lastIndex
519 ${'12'} | ${['1']} | ${1}
520 ${'112'} | ${['1']} | ${1}
521 ${'1123'} | ${['1']} | ${1}
522 ${'2'} | ${undefined} | ${0}
523 ${'1'} | ${undefined} | ${0}
524 ${'2'} | ${undefined} | ${0}
525 ${'_'} | ${undefined} | ${0}
526 `(
527 'should return $result when $input is passed',
528 ({ input, result, lastIndex }) => {
529 expectToParse(node, input, result, lastIndex);
530 }
531 );
532});
533
534describe('negative lookahead group', () => {
535 const node = match('node')`(?! ${/1/}) ${/\d/}`;
536 it.each`
537 input | result | lastIndex
538 ${'2'} | ${['2']} | ${1}
539 ${'23'} | ${['2']} | ${1}
540 ${'1'} | ${undefined} | ${0}
541 ${'1'} | ${undefined} | ${0}
542 ${'_'} | ${undefined} | ${0}
543 `(
544 'should return $result when $input is passed',
545 ({ input, result, lastIndex }) => {
546 expectToParse(node, input, result, lastIndex);
547 }
548 );
549});
550
551describe('negative lookahead shorthand', () => {
552 const node = match('node')`!${/1/} ${/\d/}`;
553 it.each`
554 input | result | lastIndex
555 ${'2'} | ${['2']} | ${1}
556 ${'23'} | ${['2']} | ${1}
557 ${'1'} | ${undefined} | ${0}
558 ${'1'} | ${undefined} | ${0}
559 ${'_'} | ${undefined} | ${0}
560 `(
561 'should return $result when $input is passed',
562 ({ input, result, lastIndex }) => {
563 expectToParse(node, input, result, lastIndex);
564 }
565 );
566});
567
568describe('longer negative lookahead group', () => {
569 const node = match('node')`${/1/} (?! ${/2/} ${/3/}) ${/\d/} ${/\d/}`;
570 it.each`
571 input | result | lastIndex
572 ${'145'} | ${['1', '4', '5']} | ${3}
573 ${'124'} | ${['1', '2', '4']} | ${3}
574 ${'123'} | ${undefined} | ${0}
575 ${'2'} | ${undefined} | ${0}
576 ${'_'} | ${undefined} | ${0}
577 `(
578 'should return $result when $input is passed',
579 ({ input, result, lastIndex }) => {
580 expectToParse(node, input, result, lastIndex);
581 }
582 );
583});
584
585describe('negative lookahead group with plus matcher', () => {
586 const node = match('node')`(?! ${/1/}+) ${/\d/}`;
587 it.each`
588 input | result | lastIndex
589 ${'2'} | ${['2']} | ${1}
590 ${'21'} | ${['2']} | ${1}
591 ${'22'} | ${['2']} | ${1}
592 ${'11'} | ${undefined} | ${0}
593 ${'1'} | ${undefined} | ${0}
594 ${'_'} | ${undefined} | ${0}
595 `(
596 'should return $result when $input is passed',
597 ({ input, result, lastIndex }) => {
598 expectToParse(node, input, result, lastIndex);
599 }
600 );
601});
602
603describe('negative lookahead group with plus group and required matcher', () => {
604 const node = match('node')`(?! ${/1/}+ ${/2/}) ${/\d/}`;
605 it.each`
606 input | result | lastIndex
607 ${'21'} | ${['2']} | ${1}
608 ${'211'} | ${['2']} | ${1}
609 ${'113'} | ${['1']} | ${1}
610 ${'1'} | ${['1']} | ${1}
611 ${'112'} | ${undefined} | ${0}
612 ${'12'} | ${undefined} | ${0}
613 ${'_'} | ${undefined} | ${0}
614 `(
615 'should return $result when $input is passed',
616 ({ input, result, lastIndex }) => {
617 expectToParse(node, input, result, lastIndex);
618 }
619 );
620});
621
622describe('interpolation parsing', () => {
623 const node = match('node')`
624 ${/1/}
625 ${interpolation((x) => (x > 1 ? x : null))}
626 ${/3/}
627 `;
628
629 it('matches interpolations', () => {
630 const expected = ['1', 2, '3'];
631 expected.tag = 'node';
632 expect(parse(node)`1${2}3`).toEqual(expected);
633 });
634
635 it('does not match invalid inputs', () => {
636 expect(parse(node)`13`).toBe(undefined);
637 expect(parse(node)`13${2}`).toBe(undefined);
638 expect(parse(node)`${2}13`).toBe(undefined);
639 expect(parse(node)`1${1}3`).toBe(undefined);
640 });
641});
642
643describe('string matching', () => {
644 const node = match('node')`
645 ${'1'}
646 ${'2'}
647 `;
648
649 it('matches strings', () => {
650 const expected = ['1', '2'];
651 expected.tag = 'node';
652 expect(parse(node)('12')).toEqual(expected);
653 expect(parse(node)('13')).toBe(undefined);
654 });
655});