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