Mirror: The magical sticky regex-based parser generator 馃
at v1.0.2 18 kB view raw
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});