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