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
29describe('optional matcher', () => {
30 const node = match('node')`${/1/}?`;
31 it.each`
32 input | result
33 ${'1'} | ${['1']}
34 ${'_'} | ${[]}
35 ${''} | ${[]}
36 `('should return $result when $input is passed', ({ input, result }) => {
37 expectToParse(node, input, result);
38 });
39});
40
41describe('star matcher', () => {
42 const node = match('node')`${/1/}*`;
43 it.each`
44 input | result
45 ${'1'} | ${['1']}
46 ${'11'} | ${['1', '1']}
47 ${'111'} | ${['1', '1', '1']}
48 ${'_'} | ${[]}
49 ${''} | ${[]}
50 `('should return $result when "$input" is passed', ({ input, result }) => {
51 expectToParse(node, input, result);
52 });
53});
54
55describe('plus matcher', () => {
56 const node = match('node')`${/1/}+`;
57 it.each`
58 input | result
59 ${'1'} | ${['1']}
60 ${'11'} | ${['1', '1']}
61 ${'111'} | ${['1', '1', '1']}
62 ${'_'} | ${undefined}
63 ${''} | ${undefined}
64 `('should return $result when "$input" is passed', ({ input, result }) => {
65 expectToParse(node, input, result);
66 });
67});
68
69describe('optional then required matcher', () => {
70 const node = match('node')`${/1/}? ${/2/}`;
71 it.each`
72 input | result
73 ${'12'} | ${['1', '2']}
74 ${'2'} | ${['2']}
75 ${''} | ${undefined}
76 `('should return $result when $input is passed', ({ input, result }) => {
77 expectToParse(node, input, result);
78 });
79});
80
81describe('star then required matcher', () => {
82 const node = match('node')`${/1/}* ${/2/}`;
83 it.each`
84 input | result
85 ${'12'} | ${['1', '2']}
86 ${'112'} | ${['1', '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('plus 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'} | ${undefined}
101 ${''} | ${undefined}
102 `('should return $result when $input is passed', ({ input, result }) => {
103 expectToParse(node, input, result);
104 });
105});
106
107describe('optional group then required matcher', () => {
108 const node = match('node')`(${/1/} ${/2/})? ${/3/}`;
109 it.each`
110 input | result
111 ${'123'} | ${['1', '2', '3']}
112 ${'3'} | ${['3']}
113 ${'_'} | ${undefined}
114 `('should return $result when $input is passed', ({ input, result }) => {
115 expectToParse(node, input, result);
116 });
117});
118
119describe('star group then required matcher', () => {
120 const node = match('node')`(${/1/} ${/2/})* ${/3/}`;
121 it.each`
122 input | result
123 ${'123'} | ${['1', '2', '3']}
124 ${'12123'} | ${['1', '2', '1', '2', '3']}
125 ${'3'} | ${['3']}
126 ${'13'} | ${undefined}
127 ${'_'} | ${undefined}
128 `('should return $result when $input is passed', ({ input, result }) => {
129 expectToParse(node, input, result);
130 });
131});
132
133describe('plus 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'} | ${undefined}
140 ${'13'} | ${undefined}
141 ${'_'} | ${undefined}
142 `('should return $result when $input is passed', ({ input, result }) => {
143 expectToParse(node, input, result);
144 });
145});
146
147describe('optional group with nested optional matcher, then required matcher', () => {
148 const node = match('node')`(${/1/}? ${/2/})? ${/3/}`;
149 it.each`
150 input | result
151 ${'123'} | ${['1', '2', '3']}
152 ${'23'} | ${['2', '3']}
153 ${'3'} | ${['3']}
154 ${'13'} | ${undefined}
155 ${'_'} | ${undefined}
156 `('should return $result when $input is passed', ({ input, result }) => {
157 expectToParse(node, input, result);
158 });
159});
160
161describe('star group with nested optional matcher, then required matcher', () => {
162 const node = match('node')`(${/1/}? ${/2/})* ${/3/}`;
163 it.each`
164 input | result
165 ${'123'} | ${['1', '2', '3']}
166 ${'23'} | ${['2', '3']}
167 ${'223'} | ${['2', '2', '3']}
168 ${'2123'} | ${['2', '1', '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('plus 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'} | ${undefined}
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 plus matcher, then required matcher', () => {
194 const node = match('node')`(${/1/}+ ${/2/})+ ${/3/}`;
195 it.each`
196 input | result
197 ${'123'} | ${['1', '2', '3']}
198 ${'1123'} | ${['1', '1', '2', '3']}
199 ${'12123'} | ${['1', '2', '1', '2', '3']}
200 ${'121123'} | ${['1', '2', '1', '1', '2', '3']}
201 ${'3'} | ${undefined}
202 ${'23'} | ${undefined}
203 ${'13'} | ${undefined}
204 ${'_'} | ${undefined}
205 `('should return $result when $input is passed', ({ input, result }) => {
206 expectToParse(node, input, result);
207 });
208});
209
210describe('plus group with nested required and plus matcher, then required matcher', () => {
211 const node = match('node')`(${/1/} ${/2/}+)+ ${/3/}`;
212 it.each`
213 input | result
214 ${'123'} | ${['1', '2', '3']}
215 ${'1223'} | ${['1', '2', '2', '3']}
216 ${'122123'} | ${['1', '2', '2', '1', '2', '3']}
217 ${'13'} | ${undefined}
218 ${'_'} | ${undefined}
219 `('should return $result when $input is passed', ({ input, result }) => {
220 expectToParse(node, input, result);
221 });
222});
223
224describe('nested plus group with nested required and plus matcher, then required matcher or alternate', () => {
225 const node = match('node')`(${/1/} ${/2/}+)+ ${/3/} | ${/1/}`;
226 it.each`
227 input | result
228 ${'123'} | ${['1', '2', '3']}
229 ${'1223'} | ${['1', '2', '2', '3']}
230 ${'122123'} | ${['1', '2', '2', '1', '2', '3']}
231 ${'1'} | ${['1']}
232 ${'13'} | ${['1']}
233 ${'_'} | ${undefined}
234 `('should return $result when $input is passed', ({ input, result }) => {
235 expectToParse(node, input, result);
236 });
237});
238
239describe('nested plus group with nested required and plus matcher, then alternate', () => {
240 const node = match('node')`(${/1/} ${/2/}+)+ (${/3/} | ${/4/})`;
241 it.each`
242 input | result
243 ${'123'} | ${['1', '2', '3']}
244 ${'124'} | ${['1', '2', '4']}
245 ${'1223'} | ${['1', '2', '2', '3']}
246 ${'1224'} | ${['1', '2', '2', '4']}
247 ${'1'} | ${undefined}
248 ${'13'} | ${undefined}
249 ${'_'} | ${undefined}
250 `('should return $result when $input is passed', ({ input, result }) => {
251 expectToParse(node, input, result);
252 });
253});
254
255describe('regular alternate', () => {
256 const node = match('node')`${/1/} | ${/2/} | ${/3/} | ${/4/}`;
257 it.each`
258 input | result
259 ${'1'} | ${['1']}
260 ${'2'} | ${['2']}
261 ${'3'} | ${['3']}
262 ${'4'} | ${['4']}
263 ${'_'} | ${undefined}
264 `('should return $result when $input is passed', ({ input, result }) => {
265 expectToParse(node, input, result);
266 });
267});
268
269describe('nested alternate in nested alternate in alternate', () => {
270 const node = match('node')`((${/1/} | ${/2/}) | ${/3/}) | ${/4/}`;
271 it.each`
272 input | result
273 ${'1'} | ${['1']}
274 ${'2'} | ${['2']}
275 ${'3'} | ${['3']}
276 ${'4'} | ${['4']}
277 ${'_'} | ${undefined}
278 `('should return $result when $input is passed', ({ input, result }) => {
279 expectToParse(node, input, result);
280 });
281});
282
283describe('alternate after required matcher', () => {
284 const node = match('node')`${/1/} (${/2/} | ${/3/})`;
285 it.each`
286 input | result
287 ${'12'} | ${['1', '2']}
288 ${'13'} | ${['1', '3']}
289 ${'14'} | ${undefined}
290 ${'3'} | ${undefined}
291 ${'_'} | ${undefined}
292 `('should return $result when $input is passed', ({ input, result }) => {
293 expectToParse(node, input, result);
294 });
295});
296
297describe('alternate with star group and required matcher after required matcher', () => {
298 const node = match('node')`${/1/} (${/2/}* ${/3/} | ${/4/})`;
299 it.each`
300 input | result
301 ${'123'} | ${['1', '2', '3']}
302 ${'1223'} | ${['1', '2', '2', '3']}
303 ${'13'} | ${['1', '3']}
304 ${'14'} | ${['1', '4']}
305 ${'12'} | ${undefined}
306 ${'15'} | ${undefined}
307 ${'_'} | ${undefined}
308 `('should return $result when $input is passed', ({ input, result }) => {
309 expectToParse(node, input, result);
310 });
311});
312
313describe('alternate with plus 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 ${'14'} | ${['1', '4']}
320 ${'13'} | ${undefined}
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 optional 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 ${'13'} | ${['1', '3']}
335 ${'14'} | ${['1', '4']}
336 ${'12'} | ${undefined}
337 ${'15'} | ${undefined}
338 ${'_'} | ${undefined}
339 `('should return $result when $input is passed', ({ input, result }) => {
340 expectToParse(node, input, result);
341 });
342});
343
344describe('non-capturing group', () => {
345 const node = match('node')`${/1/} (?: ${/2/}+)`;
346 it.each`
347 input | result | lastIndex
348 ${'12'} | ${['1']} | ${2}
349 ${'122'} | ${['1']} | ${3}
350 ${'13'} | ${undefined} | ${0}
351 ${'1'} | ${undefined} | ${0}
352 ${'_'} | ${undefined} | ${0}
353 `(
354 'should return $result when $input is passed',
355 ({ input, result, lastIndex }) => {
356 expectToParse(node, input, result, lastIndex);
357 }
358 );
359});
360
361describe('non-capturing group with plus matcher, then required matcher', () => {
362 const node = match('node')`(?: ${/1/}+) ${/2/}`;
363 it.each`
364 input | result | lastIndex
365 ${'12'} | ${['2']} | ${2}
366 ${'112'} | ${['2']} | ${3}
367 ${'1'} | ${undefined} | ${0}
368 ${'13'} | ${undefined} | ${0}
369 ${'2'} | ${undefined} | ${0}
370 ${'_'} | ${undefined} | ${0}
371 `(
372 'should return $result when $input is passed',
373 ({ input, result, lastIndex }) => {
374 expectToParse(node, input, result, lastIndex);
375 }
376 );
377});
378
379describe('non-capturing group with star group and required matcher, then required matcher', () => {
380 const node = match('node')`(?: ${/1/}* ${/2/}) ${/3/}`;
381 it.each`
382 input | result | lastIndex
383 ${'123'} | ${['3']} | ${3}
384 ${'1123'} | ${['3']} | ${4}
385 ${'23'} | ${['3']} | ${2}
386 ${'13'} | ${undefined} | ${0}
387 ${'2'} | ${undefined} | ${0}
388 ${'_'} | ${undefined} | ${0}
389 `(
390 'should return $result when $input is passed',
391 ({ input, result, lastIndex }) => {
392 expectToParse(node, input, result, lastIndex);
393 }
394 );
395});
396
397describe('non-capturing group with plus group and required matcher, then required matcher', () => {
398 const node = match('node')`(?: ${/1/}+ ${/2/}) ${/3/}`;
399 it.each`
400 input | result | lastIndex
401 ${'123'} | ${['3']} | ${3}
402 ${'1123'} | ${['3']} | ${4}
403 ${'23'} | ${undefined} | ${0}
404 ${'13'} | ${undefined} | ${0}
405 ${'2'} | ${undefined} | ${0}
406 ${'_'} | ${undefined} | ${0}
407 `(
408 'should return $result when $input is passed',
409 ({ input, result, lastIndex }) => {
410 expectToParse(node, input, result, lastIndex);
411 }
412 );
413});
414
415describe('non-capturing group with optional and required matcher, then required matcher', () => {
416 const node = match('node')`(?: ${/1/}? ${/2/}) ${/3/}`;
417 it.each`
418 input | result | lastIndex
419 ${'123'} | ${['3']} | ${3}
420 ${'23'} | ${['3']} | ${2}
421 ${'13'} | ${undefined} | ${0}
422 ${'2'} | ${undefined} | ${0}
423 ${'_'} | ${undefined} | ${0}
424 `(
425 'should return $result when $input is passed',
426 ({ input, result, lastIndex }) => {
427 expectToParse(node, input, result, lastIndex);
428 }
429 );
430});
431
432describe('positive lookahead group', () => {
433 const node = match('node')`(?= ${/1/}) ${/\d/}`;
434 it.each`
435 input | result | lastIndex
436 ${'1'} | ${['1']} | ${1}
437 ${'13'} | ${['1']} | ${1}
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('positive lookahead group with plus matcher', () => {
449 const node = match('node')`(?= ${/1/}+) ${/\d/}`;
450 it.each`
451 input | result | lastIndex
452 ${'1'} | ${['1']} | ${1}
453 ${'11'} | ${['1']} | ${1}
454 ${'12'} | ${['1']} | ${1}
455 ${'22'} | ${undefined} | ${0}
456 ${'2'} | ${undefined} | ${0}
457 ${'_'} | ${undefined} | ${0}
458 `(
459 'should return $result when $input is passed',
460 ({ input, result, lastIndex }) => {
461 expectToParse(node, input, result, lastIndex);
462 }
463 );
464});
465
466describe('positive lookahead group with plus group and required matcher', () => {
467 const node = match('node')`(?= ${/1/}+ ${/2/}) ${/\d/}`;
468 it.each`
469 input | result | lastIndex
470 ${'12'} | ${['1']} | ${1}
471 ${'112'} | ${['1']} | ${1}
472 ${'1123'} | ${['1']} | ${1}
473 ${'2'} | ${undefined} | ${0}
474 ${'1'} | ${undefined} | ${0}
475 ${'2'} | ${undefined} | ${0}
476 ${'_'} | ${undefined} | ${0}
477 `(
478 'should return $result when $input is passed',
479 ({ input, result, lastIndex }) => {
480 expectToParse(node, input, result, lastIndex);
481 }
482 );
483});
484
485describe('negative lookahead group', () => {
486 const node = match('node')`(?! ${/1/}) ${/\d/}`;
487 it.each`
488 input | result | lastIndex
489 ${'2'} | ${['2']} | ${1}
490 ${'23'} | ${['2']} | ${1}
491 ${'1'} | ${undefined} | ${0}
492 ${'1'} | ${undefined} | ${0}
493 ${'_'} | ${undefined} | ${0}
494 `(
495 'should return $result when $input is passed',
496 ({ input, result, lastIndex }) => {
497 expectToParse(node, input, result, lastIndex);
498 }
499 );
500});
501
502describe('longer negative lookahead group', () => {
503 const node = match('node')`${/1/} (?! ${/2/} ${/3/}) ${/\d/} ${/\d/}`;
504 it.each`
505 input | result | lastIndex
506 ${'145'} | ${['1', '4', '5']} | ${3}
507 ${'124'} | ${['1', '2', '4']} | ${3}
508 ${'123'} | ${undefined} | ${0}
509 ${'2'} | ${undefined} | ${0}
510 ${'_'} | ${undefined} | ${0}
511 `(
512 'should return $result when $input is passed',
513 ({ input, result, lastIndex }) => {
514 expectToParse(node, input, result, lastIndex);
515 }
516 );
517});
518
519describe('negative lookahead group with plus matcher', () => {
520 const node = match('node')`(?! ${/1/}+) ${/\d/}`;
521 it.each`
522 input | result | lastIndex
523 ${'2'} | ${['2']} | ${1}
524 ${'21'} | ${['2']} | ${1}
525 ${'22'} | ${['2']} | ${1}
526 ${'11'} | ${undefined} | ${0}
527 ${'1'} | ${undefined} | ${0}
528 ${'_'} | ${undefined} | ${0}
529 `(
530 'should return $result when $input is passed',
531 ({ input, result, lastIndex }) => {
532 expectToParse(node, input, result, lastIndex);
533 }
534 );
535});
536
537describe('negative lookahead group with plus group and required matcher', () => {
538 const node = match('node')`(?! ${/1/}+ ${/2/}) ${/\d/}`;
539 it.each`
540 input | result | lastIndex
541 ${'21'} | ${['2']} | ${1}
542 ${'211'} | ${['2']} | ${1}
543 ${'113'} | ${['1']} | ${1}
544 ${'1'} | ${['1']} | ${1}
545 ${'112'} | ${undefined} | ${0}
546 ${'12'} | ${undefined} | ${0}
547 ${'_'} | ${undefined} | ${0}
548 `(
549 'should return $result when $input is passed',
550 ({ input, result, lastIndex }) => {
551 expectToParse(node, input, result, lastIndex);
552 }
553 );
554});
555
556describe('interpolation parsing', () => {
557 const node = match('node')`
558 ${/1/}
559 ${interpolation((x) => x > 1 && x)}
560 ${/3/}
561 `;
562
563 it('matches interpolations', () => {
564 const expected = ['1', 2, '3'];
565 expected.tag = 'node';
566 expect(parse(node)`1${2}3`).toEqual(expected);
567 });
568
569 it('does not match invalid inputs', () => {
570 expect(parse(node)`13`).toBe(undefined);
571 expect(parse(node)`13${2}`).toBe(undefined);
572 expect(parse(node)`${2}13`).toBe(undefined);
573 expect(parse(node)`1${1}3`).toBe(undefined);
574 });
575});