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