1 module ctpg.combinators; 2 3 import std.conv : to; 4 import std.typecons : Tuple, tuple; 5 import std.traits : CommonType, isArray; 6 import std.range : hasSlicing; 7 8 import ctpg : parse; 9 10 import ctpg.for_unittest; 11 import ctpg.parse_result : ParseResult, getParseResultType, getParseResultTypes; 12 import ctpg.parser_kind : ParserKind, ParserKinds; 13 import ctpg.input : Input; 14 import ctpg.caller : Caller; 15 import ctpg.none : None; 16 import ctpg.macro_ : MAKE_RESULT; 17 import ctpg.option : Option; 18 import ctpg.is_wrapper : isSameType; 19 20 import parsers = ctpg.parsers; 21 22 import compile_time_unittest : enableCompileTimeUnittest; 23 mixin enableCompileTimeUnittest; 24 25 import selflinoin : makeCompilationErrorMessage; 26 27 28 template untuple(alias parser) 29 { 30 template build(alias kind, SrcType) 31 { 32 static if(kind.hasValue) 33 { 34 static if(getParseResultType!(parser.build!(kind, SrcType)).length == 1) 35 { 36 mixin MAKE_RESULT!q{ getParseResultType!(parser.build!(kind, SrcType)).Types[0] }; 37 38 Result parse(Input!SrcType input, in Caller caller) 39 { 40 Result result; 41 42 with(parser.build!(kind, SrcType).parse(input, caller)) 43 { 44 static if(kind.hasError) 45 { 46 result.error = error; 47 } 48 49 if(match) 50 { 51 result.match = true; 52 result.nextInput = nextInput; 53 result.value = value[0]; 54 } 55 } 56 57 return result; 58 } 59 } 60 else 61 { 62 alias parse = parser.build!(kind, SrcType).parse; 63 } 64 } 65 else 66 { 67 alias parse = parser.build!(kind, SrcType).parse; 68 } 69 } 70 } 71 72 debug(ctpg) unittest 73 { 74 foreach(conv; convs) foreach(kind; ParserKinds) 75 { 76 with(parse!(untuple!(TestParser!(tuple("hoge", "fuga"))), kind)(conv!"input")) 77 { 78 assert(match == true); 79 static if(kind.hasValue) assert(value == tuple("hoge", "fuga")); 80 assert(nextInput.source == conv!"input"); 81 assert(nextInput.position == 0); 82 assert(nextInput.line == 0); 83 } 84 85 with(parse!(untuple!(TestParser!(tuple("hoge"))), kind)(conv!"input")) 86 { 87 assert(match == true); 88 static if(kind.hasValue) assert(value == "hoge"); 89 assert(nextInput.source == conv!"input"); 90 assert(nextInput.position == 0); 91 assert(nextInput.line == 0); 92 } 93 } 94 } 95 96 97 template sequence(parsers...) 98 { 99 static assert(parsers.length > 0); 100 101 template build(alias kind, SrcType) 102 { 103 static if(parsers.length == 1) 104 { 105 alias T = getParseResultType!(parsers[0].build!(kind, SrcType)); 106 107 static if(is(T == None)) mixin MAKE_RESULT!q{ Tuple!() }; 108 else mixin MAKE_RESULT!q{ Tuple!T }; 109 110 Result parse(Input!SrcType input, in Caller caller) 111 { 112 Result result; 113 114 auto head = parsers[0].build!(kind, SrcType).parse(input, caller); 115 116 static if(kind.hasError) 117 { 118 result.error = head.error; 119 } 120 121 if(!head.match) 122 { 123 return result; 124 } 125 126 result.match = true; 127 result.nextInput = head.nextInput; 128 129 static if(kind.hasValue && !is(T == None)) 130 { 131 result.value = tuple(head.value); 132 } 133 134 return result; 135 } 136 } 137 else static if(parsers.length > 1) 138 { 139 alias T = getParseResultType!(parsers[0].build!(kind, SrcType)); 140 141 static if(is(T == None)) mixin MAKE_RESULT!q{ getParseResultType!(sequence!(parsers[1..$]).build!(kind, SrcType)) }; 142 else mixin MAKE_RESULT!q{ Tuple!(T, getParseResultType!(sequence!(parsers[1..$]).build!(kind, SrcType)).Types) }; 143 144 Result parse(Input!SrcType input, in Caller caller) 145 { 146 Result result; 147 148 auto head = parsers[0].build!(kind, SrcType).parse(input, caller); 149 150 static if(kind.hasError) 151 { 152 result.error = head.error; 153 } 154 155 if(!head.match) 156 { 157 return result; 158 } 159 160 auto tail = sequence!(parsers[1..$]).build!(kind, SrcType).parse(head.nextInput, caller); 161 162 static if(kind.hasError) 163 { 164 if(result.error.position <= tail.error.position) 165 { 166 result.error = tail.error; 167 } 168 } 169 170 if(!tail.match) 171 { 172 return result; 173 } 174 175 result.match = true; 176 result.nextInput = tail.nextInput; 177 178 static if(kind.hasValue) 179 { 180 static if(is(T == None)) result.value = tail.value; 181 else result.value = tuple(head.value, tail.value.field); 182 } 183 184 return result; 185 } 186 } 187 } 188 } 189 190 debug(ctpg) unittest 191 { 192 foreach(conv; convs) foreach(kind; ParserKinds) 193 { 194 with(parse!(sequence!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"hogefugahoge")) 195 { 196 assert(match == true); 197 static if(kind.hasValue) assert(value == tuple("hoge", "fuga")); 198 assert(nextInput.source == conv!"hoge"); 199 assert(nextInput.position == 8); 200 assert(nextInput.line == 0); 201 } 202 203 with(parse!(sequence!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"hoge")) 204 { 205 assert(match == false); 206 static if(kind.hasError) assert(error.msg == "'fuga' expected"); 207 static if(kind.hasError) assert(error.position == 4); 208 } 209 210 with(parse!(sequence!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"fuga")) 211 { 212 assert(match == false); 213 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 214 static if(kind.hasError) assert(error.position == 0); 215 } 216 217 with(parse!(sequence!(parsers.string_!"hoge", TestParser!(None()), parsers.string_!"fuga"), kind)(conv!"hogefuga")) 218 { 219 assert(match == true); 220 static if(kind.hasValue) assert(value == tuple("hoge", "fuga")); 221 assert(nextInput.source == conv!""); 222 assert(nextInput.position == 8); 223 assert(nextInput.line == 0); 224 } 225 } 226 } 227 228 229 230 template choiceWithFileLine(string file, size_t line, parsers...) 231 { 232 static assert(parsers.length > 0); 233 234 template build(alias kind, SrcType) 235 { 236 static if (kind.hasValue && isSameType!(CommonType!(getParseResultTypes!(kind, SrcType, parsers)), void)) 237 { 238 pragma(msg, makeCompilationErrorMessage("incompatible types: " ~ getParseResultTypes!(kind, SrcType, parsers).stringof, file, line)); 239 static assert(false); 240 } 241 242 static if(parsers.length == 1) 243 { 244 mixin MAKE_RESULT!q{ getParseResultType!(parsers[0].build!(kind, SrcType)) }; 245 246 Result parse(Input!SrcType input, in Caller caller) 247 { 248 Result result; 249 250 auto head = parsers[0].build!(kind, SrcType).parse(input, caller); 251 252 static if(kind.hasError) 253 { 254 result.error = head.error; 255 } 256 257 if(head.match) 258 { 259 result.match = true; 260 result.nextInput = head.nextInput; 261 262 static if(kind.hasValue) 263 { 264 result.value = head.value; 265 } 266 } 267 268 return result; 269 } 270 } 271 else static if(parsers.length > 1) 272 { 273 mixin MAKE_RESULT!q{ CommonType!(getParseResultType!(parsers[0].build!(kind, SrcType)), getParseResultType!(choice!(parsers[1..$]).build!(kind, SrcType))) }; 274 275 Result parse(Input!SrcType input, in Caller caller) 276 { 277 Result result; 278 279 auto head = parsers[0].build!(kind, SrcType).parse(input.save, caller); 280 281 static if(kind.hasError) 282 { 283 result.error = head.error; 284 } 285 286 if(head.match) 287 { 288 result.match = true; 289 result.nextInput = head.nextInput; 290 291 static if(kind.hasValue) 292 { 293 result.value = head.value; 294 } 295 296 return result; 297 } 298 299 auto tail = choice!(parsers[1..$]).build!(kind, SrcType).parse(input, caller); 300 301 static if(kind.hasError) 302 { 303 if(result.error.position <= tail.error.position) 304 { 305 result.error = tail.error; 306 } 307 } 308 309 if(tail.match) 310 { 311 result.match = true; 312 result.nextInput = tail.nextInput; 313 314 static if(kind.hasValue) 315 { 316 result.value = tail.value; 317 } 318 } 319 320 return result; 321 } 322 } 323 } 324 } 325 326 template choice(parsers...) 327 { 328 alias choice = choiceWithFileLine!("", 0, parsers); 329 } 330 331 debug(ctpg) unittest 332 { 333 foreach(conv; convs) foreach(kind; ParserKinds) 334 { 335 with(parse!(choice!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"hoge")) 336 { 337 assert(match == true); 338 static if(kind.hasValue) assert(value == "hoge"); 339 assert(nextInput.source == conv!""); 340 assert(nextInput.position == 4); 341 assert(nextInput.line == 0); 342 } 343 344 with(parse!(choice!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"fuga")) 345 { 346 assert(match == true); 347 static if(kind.hasValue) assert(value == "fuga"); 348 assert(nextInput.source == conv!""); 349 assert(nextInput.position == 4); 350 assert(nextInput.line == 0); 351 } 352 353 with(parse!(choice!(parsers.string_!"hoge", parsers.string_!"fuga"), kind)(conv!"piyo")) 354 { 355 assert(match == false); 356 static if(kind.hasError) assert(error.msg == "'fuga' expected"); 357 static if(kind.hasError) assert(error.position == 0); 358 } 359 360 with(parse!(choice!(sequence!(parsers.string_!"hoge", parsers.string_!"piyo"), parsers.string_!"fuga"), ParserKind!(false, kind.hasError))(conv!"hoge")) 361 { 362 assert(match == false); 363 static if(kind.hasError) assert(error.msg == "'piyo' expected"); 364 static if(kind.hasError) assert(error.position == 4); 365 } 366 367 debug(ctpgCompilesTest) 368 { 369 static if(kind.hasValue) 370 { 371 static assert(!__traits(compiles, parse!(choice!(TestParser!"hoge", TestParser!1), kind)(conv!""))); 372 } 373 else 374 { 375 static assert(__traits(compiles, parse!(choice!(TestParser!"hoge", TestParser!1), kind)(conv!""))); 376 } 377 } 378 } 379 } 380 381 382 template more(size_t n, alias parser, alias sep = success!()) 383 { 384 template build(alias kind, SrcType) 385 { 386 mixin MAKE_RESULT!q{ getParseResultType!(parser.build!(kind, SrcType))[] }; 387 388 Result parse(Input!SrcType input, in Caller caller) 389 { 390 Result result; 391 size_t size = 1; 392 Input!SrcType next = input.save; 393 394 auto head = parser.build!(kind, SrcType).parse(next, caller); 395 396 static if(kind.hasError) 397 { 398 result.error = head.error; 399 } 400 401 if(!head.match) 402 { 403 if(n == 0) 404 { 405 result.match = true; 406 result.nextInput = input; 407 } 408 409 return result; 410 } 411 412 static if(kind.hasValue) 413 { 414 result.value ~= head.value; 415 } 416 417 next = head.nextInput.save; 418 419 while(true) 420 { 421 auto r1 = sep.build!(ParserKind!(false, kind.hasError), SrcType).parse(next, caller); 422 423 static if(kind.hasError) 424 { 425 if(result.error.position <= r1.error.position) 426 { 427 result.error = r1.error; 428 } 429 } 430 431 if(r1.match) 432 { 433 auto r2 = parser.build!(kind, SrcType).parse(r1.nextInput, caller); 434 435 static if(kind.hasError) 436 { 437 if(result.error.position <= r2.error.position) 438 { 439 result.error = r2.error; 440 } 441 } 442 443 if(r2.match) 444 { 445 static if(kind.hasValue) 446 { 447 result.value ~= r2.value; 448 } 449 450 ++size; 451 next = r2.nextInput; 452 } 453 else 454 { 455 if(size < n) 456 { 457 return result; 458 } 459 460 break; 461 } 462 } 463 else 464 { 465 if(size < n) 466 { 467 return result; 468 } 469 470 break; 471 } 472 } 473 474 result.match = true; 475 result.nextInput = next; 476 477 return result; 478 } 479 } 480 } 481 482 template more0(alias parser, alias sep = parsers.success!()) 483 { 484 alias more0 = more!(0, parser, sep); 485 } 486 487 488 template more1(alias parser, alias sep = parsers.success!()) 489 { 490 alias more1 = more!(1, parser, sep); 491 } 492 493 debug(ctpg) unittest 494 { 495 foreach(conv; convs) foreach(kind; ParserKinds) 496 { 497 with(parse!(more!(0, parsers.string_!"hoge", parsers.string_!","), kind)(conv!"")) 498 { 499 assert(match == true); 500 static if(kind.hasValue) assert(value == []); 501 assert(nextInput.source == conv!""); 502 assert(nextInput.position == 0); 503 assert(nextInput.line == 0); 504 } 505 506 with(parse!(more!(0, parsers.string_!"hoge", parsers.string_!","), kind)(conv!"hoge,hoge,hoge")) 507 { 508 assert(match == true); 509 static if(kind.hasValue) assert(value == ["hoge", "hoge", "hoge"]); 510 assert(nextInput.source == conv!""); 511 assert(nextInput.position == 14); 512 assert(nextInput.line == 0); 513 } 514 515 with(parse!(more!(0, parsers.string_!"hoge", parsers.string_!","), kind)(conv!"hoge,hoge,hoge,")) 516 { 517 assert(match == true); 518 static if(kind.hasValue) assert(value == ["hoge", "hoge", "hoge"]); 519 assert(nextInput.source == conv!","); 520 assert(nextInput.position == 14); 521 assert(nextInput.line == 0); 522 } 523 524 with(parse!(more!(4, parsers.string_!"hoge", parsers.string_!","), kind)(conv!"hoge,hoge,hoge")) 525 { 526 assert(match == false); 527 static if(kind.hasError) assert(error.msg == "',' expected"); 528 static if(kind.hasError) assert(error.position == 14); 529 } 530 531 with(parse!(more!(4, parsers.string_!"hoge", parsers.string_!","), kind)(conv!"hoge,hoge,hoge,")) 532 { 533 assert(match == false); 534 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 535 static if(kind.hasError) assert(error.position == 15); 536 } 537 } 538 } 539 540 541 template andPred(alias parser) 542 { 543 template build(alias kind, SrcType) 544 { 545 mixin MAKE_RESULT!q{ None }; 546 547 Result parse(Input!SrcType input, in Caller caller) 548 { 549 Result result; 550 result.nextInput = input; 551 552 with(parser.build!(ParserKind!(false, kind.hasError), SrcType).parse(input.save, caller)) 553 { 554 static if(kind.hasError) 555 { 556 result.error = error; 557 } 558 559 result.match = match; 560 } 561 562 return result; 563 } 564 } 565 } 566 567 debug(ctpg) unittest 568 { 569 foreach(conv; convs) foreach(kind; ParserKinds) 570 { 571 with(parse!(andPred!(parsers.string_!"hoge"), kind)(conv!"hoge")) 572 { 573 assert(match == true); 574 static if(kind.hasValue) assert(value == None()); 575 assert(nextInput.source == conv!"hoge"); 576 assert(nextInput.position == 0); 577 assert(nextInput.line == 0); 578 } 579 580 with(parse!(andPred!(parsers.string_!"hoge"), kind)(conv!"fuga")) 581 { 582 assert(match == false); 583 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 584 static if(kind.hasError) assert(error.position == 0); 585 } 586 } 587 } 588 589 590 template notPred(alias parser) 591 { 592 template build(alias kind, SrcType) 593 { 594 mixin MAKE_RESULT!q{ None }; 595 596 Result parse(Input!SrcType input, in Caller caller) 597 { 598 Result result; 599 result.nextInput = input; 600 601 with(parser.build!(ParserKind!(false, false), SrcType).parse(input.save, caller)) 602 { 603 static if(kind.hasError) 604 { 605 result.error.msg = "failure expected"; 606 result.error.position = input.position; 607 } 608 609 result.match = !match; 610 } 611 612 return result; 613 } 614 } 615 } 616 617 debug(ctpg) unittest 618 { 619 foreach(conv; convs) foreach(kind; ParserKinds) 620 { 621 with(parse!(notPred!(parsers.string_!"hoge"), kind)(conv!"fuga")) 622 { 623 assert(match == true); 624 static if(kind.hasValue) assert(value == None()); 625 assert(nextInput.source == conv!"fuga"); 626 assert(nextInput.position == 0); 627 assert(nextInput.line == 0); 628 } 629 630 with(parse!(notPred!(parsers.string_!"hoge"), kind)(conv!"hoge")) 631 { 632 assert(match == false); 633 static if(kind.hasError) assert(error.msg == "failure expected"); 634 static if(kind.hasError) assert(error.position == 0); 635 } 636 } 637 } 638 639 640 template option(alias parser) 641 { 642 template build(alias kind, SrcType) 643 { 644 mixin MAKE_RESULT!q{ Option!(getParseResultType!(parser.build!(kind, SrcType))) }; 645 646 Result parse(Input!SrcType input, in Caller caller) 647 { 648 Result result; 649 result.match = true; 650 651 with(parser.build!(kind, SrcType).parse(input.save, caller)) 652 { 653 static if(kind.hasError) 654 { 655 result.error = error; 656 } 657 658 if(match) 659 { 660 result.nextInput = nextInput; 661 662 static if(kind.hasValue) 663 { 664 result.value.value = value; 665 result.value.some = true; 666 } 667 } 668 else 669 { 670 result.nextInput = input; 671 } 672 } 673 674 return result; 675 } 676 } 677 } 678 679 debug(ctpg) unittest 680 { 681 foreach(conv; convs) foreach(kind; ParserKinds) 682 { 683 with(parse!(option!(parsers.string_!"hoge"), kind)(conv!"fuga")) 684 { 685 assert(match == true); 686 static if(kind.hasValue) assert(value.some == false); 687 assert(nextInput.source == conv!"fuga"); 688 assert(nextInput.position == 0); 689 assert(nextInput.line == 0); 690 } 691 692 with(parse!(option!(parsers.string_!"hoge"), kind)(conv!"hoge")) 693 { 694 assert(match == true); 695 static if(kind.hasValue) assert(value.some == true); 696 static if(kind.hasValue) assert(value == "hoge"); 697 assert(nextInput.source == conv!""); 698 assert(nextInput.position == 4); 699 assert(nextInput.line == 0); 700 } 701 } 702 } 703 704 705 template none(alias parser) 706 { 707 template build(alias kind, SrcType) 708 { 709 static if(kind.hasValue) 710 { 711 alias Result = ParseResult!(kind, SrcType, None); 712 713 Result parse(Input!SrcType input, in Caller caller) 714 { 715 Result result; 716 717 with(parser.build!(ParserKind!(false, kind.hasError), SrcType).parse(input, caller)) 718 { 719 static if(kind.hasError) 720 { 721 result.error = error; 722 } 723 724 result.match = match; 725 726 if(match) 727 { 728 result.nextInput = nextInput; 729 } 730 } 731 732 return result; 733 } 734 } 735 else 736 { 737 alias parse = parser.build!(kind, SrcType).parse; 738 } 739 } 740 } 741 742 debug(ctpg) unittest 743 { 744 foreach(conv; convs) foreach(kind; ParserKinds) 745 { 746 with(parse!(none!(parsers.string_!"hoge"), kind)(conv!"hoge")) 747 { 748 assert(match == true); 749 static if(kind.hasValue) assert(value == None()); 750 assert(nextInput.source == conv!""); 751 assert(nextInput.position == 4); 752 assert(nextInput.line == 0); 753 } 754 755 with(parse!(none!(parsers.string_!"hoge"), kind)(conv!"fuga")) 756 { 757 assert(match == false); 758 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 759 static if(kind.hasError) assert(error.position == 0); 760 } 761 } 762 } 763 764 765 template convert(alias parser, alias converter, size_t line = 0, string file = "") 766 { 767 template build(alias kind, SrcType) 768 { 769 static if(kind.hasValue) 770 { 771 alias T = getParseResultType!(parser.build!(kind, SrcType)); 772 773 static if(is(converter == class ) && __traits(compiles, new converter(T.init.field))) alias ConverterType = converter; 774 else static if(is(converter == class ) && __traits(compiles, new converter(T.init ))) alias ConverterType = converter; 775 else static if(is(converter == struct) && __traits(compiles, converter(T.init.field))) alias ConverterType = converter; 776 else static if(is(converter == struct) && __traits(compiles, converter(T.init ))) alias ConverterType = converter; 777 else static if( __traits(compiles, converter(T.init.field))) alias ConverterType = typeof(converter(T.init.field)); 778 else static if( __traits(compiles, converter(T.init ))) alias ConverterType = typeof(converter(T.init )); 779 else alias ConverterType = void; 780 781 static if(is(ConverterType == void)) 782 { 783 pragma(msg, makeCompilationErrorMessage("Cannot call '" ~ converter.stringof ~ "' using '>>' with types: " ~ T.stringof, file, line)); 784 static assert(false); 785 } 786 787 mixin MAKE_RESULT!q{ ConverterType }; 788 789 static Result parse(Input!SrcType input, in Caller caller) 790 { 791 Result result; 792 793 with(parser.build!(kind, SrcType).parse(input, caller)) 794 { 795 static if(kind.hasError) 796 { 797 result.error = error; 798 } 799 800 if(match) 801 { 802 result.match = true; 803 result.nextInput = nextInput; 804 805 static if(kind.hasValue) 806 { 807 static if(is(converter == class ) && __traits(compiles, new converter(T.init.field))) result.value = new converter(value.field); 808 else static if(is(converter == class ) && __traits(compiles, new converter(T.init ))) result.value = new converter(value ); 809 else static if( __traits(compiles, converter(T.init.field))) result.value = converter(value.field); 810 else static if( __traits(compiles, converter(T.init ))) result.value = converter(value ); 811 } 812 } 813 } 814 815 return result; 816 } 817 } 818 else 819 { 820 alias parse = parser.build!(kind, SrcType).parse; 821 } 822 } 823 } 824 825 debug(ctpg) unittest 826 { 827 static struct Struct 828 { 829 size_t len; 830 831 this(string str) 832 { 833 len = str.length; 834 } 835 } 836 837 static class Class 838 { 839 size_t len; 840 841 this(string str) 842 { 843 len = str.length; 844 } 845 } 846 847 848 static size_t Function(string str) 849 { 850 return str.length; 851 } 852 853 static size_t TemplateFunction(T)(T str) 854 { 855 return str.length; 856 } 857 858 foreach(conv; convs) foreach(kind; ParserKinds) 859 { 860 with(parse!(convert!(parsers.string_!"hoge", Struct), kind)(conv!"hogee")) 861 { 862 assert(match == true); 863 static if(kind.hasValue) assert(value.len == 4); 864 assert(nextInput.source == conv!"e"); 865 assert(nextInput.position == 4); 866 assert(nextInput.line == 0); 867 } 868 869 with(parse!(convert!(parsers.string_!"hoge", Struct), kind)(conv!"!!!!")) 870 { 871 assert(match == false); 872 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 873 static if(kind.hasError) assert(error.position == 0); 874 } 875 876 with(parse!(convert!(parsers.string_!"hoge", Class), kind)(conv!"hogee")) 877 { 878 assert(match == true); 879 static if(kind.hasValue) assert(value.len == 4); 880 assert(nextInput.source == conv!"e"); 881 assert(nextInput.position == 4); 882 assert(nextInput.line == 0); 883 } 884 885 with(parse!(convert!(parsers.string_!"hoge", Class), kind)(conv!"!!!!")) 886 { 887 assert(match == false); 888 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 889 static if(kind.hasError) assert(error.position == 0); 890 } 891 892 with(parse!(convert!(parsers.string_!"hoge", Function), kind)(conv!"hogee")) 893 { 894 assert(match == true); 895 static if(kind.hasValue) assert(value == 4); 896 assert(nextInput.source == conv!"e"); 897 assert(nextInput.position == 4); 898 assert(nextInput.line == 0); 899 } 900 901 with(parse!(convert!(parsers.string_!"hoge", Function), kind)(conv!"!!!!")) 902 { 903 assert(match == false); 904 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 905 static if(kind.hasError) assert(error.position == 0); 906 } 907 908 with(parse!(convert!(parsers.string_!"hoge", TemplateFunction), kind)(conv!"hogee")) 909 { 910 assert(match == true); 911 static if(kind.hasValue) assert(value == 4); 912 assert(nextInput.source == conv!"e"); 913 assert(nextInput.position == 4); 914 assert(nextInput.line == 0); 915 } 916 917 with(parse!(convert!(parsers.string_!"hoge", TemplateFunction), kind)(conv!"!!!!")) 918 { 919 assert(match == false); 920 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 921 static if(kind.hasError) assert(error.position == 0); 922 } 923 924 with(parse!(convert!(parsers.string_!"hoge", (x) => x.length), kind)(conv!"hogee")) 925 { 926 assert(match == true); 927 static if(kind.hasValue) assert(value == 4); 928 assert(nextInput.source == conv!"e"); 929 assert(nextInput.position == 4); 930 assert(nextInput.line == 0); 931 } 932 933 with(parse!(convert!(parsers.string_!"hoge", (x) => x.length), kind)(conv!"!!!!")) 934 { 935 assert(match == false); 936 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 937 static if(kind.hasError) assert(error.position == 0); 938 } 939 940 with(parse!(convert!(parsers.string_!"hoge", (x){ return x.length; }), kind)(conv!"hogee")) 941 { 942 assert(match == true); 943 static if(kind.hasValue) assert(value == 4); 944 assert(nextInput.source == conv!"e"); 945 assert(nextInput.position == 4); 946 assert(nextInput.line == 0); 947 } 948 949 with(parse!(convert!(parsers.string_!"hoge", (x){ return x.length; }), kind)(conv!"!!!!")) 950 { 951 assert(match == false); 952 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 953 static if(kind.hasError) assert(error.position == 0); 954 } 955 956 debug(ctpgCompilesTest) 957 { 958 static if(kind.hasValue) 959 { 960 static assert(!__traits(compiles, parse!(convert!(parsers.string_!"hoge", (size_t x){ return x; }), kind)(conv!"hoge"))); 961 } 962 else 963 { 964 static assert(__traits(compiles, parse!(convert!(parsers.string_!"hoge", (size_t x){ return x; }), kind)(conv!"hoge"))); 965 } 966 } 967 } 968 } 969 970 971 template check(alias parser, alias checker, size_t line = 0, string file = "") 972 { 973 template build(alias kind, SrcType) 974 { 975 alias T = getParseResultType!(parser.build!(ParserKind!(true, kind.hasError), SrcType)); 976 977 static if(!__traits(compiles, checker(T.init.field)) && !__traits(compiles, checker(T.init))) 978 { 979 pragma(msg, makeCompilationErrorMessage("Cannot call '" ~ checker.stringof ~ "' using '>?' with types: " ~ T.stringof, file, line)); 980 static assert(false); 981 } 982 else static if(!is(typeof(checker(T.init.field)) == bool) && !is(typeof(checker(T.init)) == bool)) 983 { 984 pragma(msg, makeCompilationErrorMessage("'" ~ checker.stringof ~ "' does not return bool", file, line)); 985 static assert(false); 986 } 987 988 mixin MAKE_RESULT!q{ T }; 989 990 Result parse(Input!SrcType input, in Caller caller) 991 { 992 Result result; 993 994 with(parser.build!(ParserKind!(true, kind.hasError), SrcType).parse(input, caller)) 995 { 996 static if(kind.hasError) 997 { 998 result.error = error; 999 } 1000 1001 if(match) 1002 { 1003 static if(is(typeof(checker(T.init.field)) == bool)) result.match = checker(value.field); 1004 else static if(is(typeof(checker(T.init )) == bool)) result.match = checker(value ); 1005 1006 if(result.match) 1007 { 1008 result.nextInput = nextInput; 1009 1010 static if(kind.hasValue) 1011 { 1012 result.value = value; 1013 } 1014 } 1015 else 1016 { 1017 static if(kind.hasError) 1018 { 1019 result.error.msg = "passing check expected"; 1020 result.error.position = input.position; 1021 } 1022 } 1023 } 1024 } 1025 1026 return result; 1027 } 1028 } 1029 } 1030 1031 debug(ctpg) unittest 1032 { 1033 static bool Function(string str) 1034 { 1035 return str.length == 4; 1036 } 1037 1038 foreach(conv; convs) foreach(kind; ParserKinds) 1039 { 1040 with(parse!(check!(parsers.string_!"hoge", Function), kind)(conv!"hoge!")) 1041 { 1042 assert(match == true); 1043 static if(kind.hasValue) assert(value == "hoge"); 1044 assert(nextInput.source == conv!"!"); 1045 assert(nextInput.position == 4); 1046 assert(nextInput.line == 0); 1047 } 1048 1049 with(parse!(check!(parsers.string_!"hoge!", Function), kind)(conv!"hoge!")) 1050 { 1051 assert(match == false); 1052 static if(kind.hasError) assert(error.msg == "passing check expected"); 1053 static if(kind.hasError) assert(error.position == 0); 1054 } 1055 1056 with(parse!(check!(parsers.string_!"hoge", Function), kind)(conv!"fuga")) 1057 { 1058 assert(match == false); 1059 static if(kind.hasError) assert(error.msg == "'hoge' expected"); 1060 static if(kind.hasError) assert(error.position == 0); 1061 } 1062 1063 debug(ctpgCompilesTest) 1064 { 1065 static assert(!__traits(compiles, parse!(check!(parsers.string_!"hoge", (int i ){ return false; }), kind)(conv!"hoge"))); 1066 static assert(!__traits(compiles, parse!(check!(parsers.string_!"hoge", (string str){ return 0; }), kind)(conv!"hoge"))); 1067 } 1068 } 1069 } 1070 1071 1072 template inputSlice(alias parser, size_t line = 0, string file = "") 1073 { 1074 template build(alias kind, SrcType) 1075 { 1076 static if(kind.hasValue) 1077 { 1078 static if(!isArray!SrcType && !hasSlicing!SrcType) 1079 { 1080 pragma(msg, makeCompilationErrorMessage("Input type should be sliceable", file, line)); 1081 static assert(false); 1082 } 1083 1084 alias Result = ParseResult!(kind, SrcType, SrcType); 1085 1086 Result parse(Input!SrcType input, in Caller caller) 1087 { 1088 Result result; 1089 1090 with(parser.build!(ParserKind!(false, kind.hasError), SrcType).parse(input.save, caller)) 1091 { 1092 static if(kind.hasError) 1093 { 1094 result.error = error; 1095 } 1096 1097 if(match) 1098 { 1099 result.match = true; 1100 result.nextInput = nextInput; 1101 1102 static if(kind.hasValue) 1103 { 1104 result.value = input.source[0 .. nextInput.position - input.position]; 1105 } 1106 } 1107 } 1108 1109 return result; 1110 } 1111 } 1112 else 1113 { 1114 alias parse = parser.build!(kind, SrcType).parse; 1115 } 1116 } 1117 } 1118 1119 debug(ctpg) unittest 1120 { 1121 foreach(conv; convs) foreach(kind; ParserKinds) 1122 { 1123 with(parse!(inputSlice!(sequence!(parsers.string_!"hoge", parsers.string_!"fuga")), kind)(conv!"hogefugapiyo")) 1124 { 1125 assert(match == true); 1126 static if(kind.hasValue) assert(value == conv!"hogefuga"); 1127 assert(nextInput.source == conv!"piyo"); 1128 assert(nextInput.position == 8); 1129 assert(nextInput.line == 0); 1130 } 1131 1132 debug(ctpgCompilesTest) 1133 { 1134 import std.algorithm : filter; 1135 1136 static if(kind.hasValue) 1137 { 1138 static assert(!__traits(compiles, parse!(inputSlice!(parsers.string_!"hoge"), kind)(conv!"hoge".filter!"true"()))); 1139 } 1140 else 1141 { 1142 static assert(__traits(compiles, parse!(inputSlice!(parsers.string_!"hoge"), kind)(conv!"hoge".filter!"true"()))); 1143 } 1144 } 1145 } 1146 } 1147 1148 1149 template memoize(alias parser) 1150 { 1151 template build(alias kind, SrcType) 1152 { 1153 mixin MAKE_RESULT!q{ getParseResultType!(parser.build!(kind, SrcType)) }; 1154 1155 Result parse(Input!SrcType input, in Caller caller) 1156 { 1157 if(!__ctfe) 1158 { 1159 static typeof(return)[Input!SrcType] memo; 1160 1161 auto p = input in memo; 1162 if(p) 1163 { 1164 return *p; 1165 } 1166 1167 auto result = parser.build!(kind, SrcType).parse(input, caller); 1168 memo[input] = result; 1169 1170 return result; 1171 } 1172 else 1173 { 1174 return parser.build!(kind, SrcType).parse(input, caller); 1175 } 1176 } 1177 } 1178 } 1179 1180 debug(ctpg) unittest 1181 { 1182 foreach(conv; convs) foreach(kind; ParserKinds) 1183 { 1184 with(parse!(memoize!(parsers.string_!"hoge"), kind)(conv!"hogehoge")) 1185 { 1186 assert(match == true); 1187 static if(kind.hasValue) assert(value == "hoge"); 1188 assert(nextInput.source == conv!"hoge"); 1189 assert(nextInput.position == 4); 1190 assert(nextInput.line == 0); 1191 } 1192 1193 with(parse!(memoize!(parsers.string_!"hoge"), kind)(conv!"hogehoge")) 1194 { 1195 assert(match == true); 1196 static if(kind.hasValue) assert(value == "hoge"); 1197 assert(nextInput.source == conv!"hoge"); 1198 assert(nextInput.position == 4); 1199 assert(nextInput.line == 0); 1200 } 1201 1202 with(parse!(memoize!(parsers.string_!"\n\nhoge"), kind)(conv!"\n\nhogehoge")) 1203 { 1204 assert(match == true); 1205 static if(kind.hasValue) assert(value == "\n\nhoge"); 1206 assert(nextInput.source == conv!"hoge"); 1207 assert(nextInput.position == 6); 1208 assert(nextInput.line == 2); 1209 } 1210 1211 with(parse!(memoize!(parsers.string_!"\n\nhoge"), kind)(conv!"\n\nhogehoge")) 1212 { 1213 assert(match == true); 1214 static if(kind.hasValue) assert(value == "\n\nhoge"); 1215 assert(nextInput.source == conv!"hoge"); 1216 assert(nextInput.position == 6); 1217 assert(nextInput.line == 2); 1218 } 1219 } 1220 } 1221 1222 1223 template changeError(alias parser, string errorMsg) 1224 { 1225 template build(alias kind, SrcType) 1226 { 1227 static if(kind.hasError) 1228 { 1229 mixin MAKE_RESULT!q{ getParseResultType!(parser.build!(kind, SrcType)) }; 1230 1231 Result parse(Input!SrcType input, in Caller caller) 1232 { 1233 Result result; 1234 1235 with(parser.build!(ParserKind!(kind.hasValue, false), SrcType).parse(input, caller)) 1236 { 1237 if(!match) 1238 { 1239 result.error.msg = errorMsg; 1240 result.error.position = input.position; 1241 } 1242 1243 result.match = match; 1244 result.nextInput = nextInput; 1245 1246 static if(kind.hasValue) 1247 { 1248 result.value = value; 1249 } 1250 } 1251 1252 return result; 1253 } 1254 } 1255 else 1256 { 1257 alias parse = parser.build!(kind, SrcType).parse; 1258 } 1259 } 1260 } 1261 1262 debug(ctpg) unittest 1263 { 1264 foreach(conv; convs) foreach(kind; ParserKinds) 1265 { 1266 with(parse!(changeError!(parsers.string_!"hoge", "エラーだよ!!"), kind)(conv!"hoge")) 1267 { 1268 assert(match == true); 1269 assert(nextInput.source == conv!""); 1270 assert(nextInput.position == 4); 1271 assert(nextInput.line == 0); 1272 } 1273 1274 with(parse!(changeError!(parsers.string_!"hoge", "エラーだよ!!"), kind)(conv!"fuga")) 1275 { 1276 assert(match == false); 1277 static if(kind.hasError) assert(error.msg == "エラーだよ!!"); 1278 static if(kind.hasError) assert(error.position == 0); 1279 } 1280 1281 with(parse!(changeError!(sequence!(parsers.string_!"hoge", parsers.string_!"fuga"), "エラーだよ!!"), kind)(conv!"hoge")) 1282 { 1283 assert(match == false); 1284 static if(kind.hasError) assert(error.msg == "エラーだよ!!"); 1285 static if(kind.hasError) assert(error.position == 0); 1286 } 1287 } 1288 } 1289 1290 1291 template skip(alias skip, alias parser) 1292 { 1293 template build(alias kind, SrcType) 1294 { 1295 mixin MAKE_RESULT!q{ getParseResultType!(parser.build!(kind, SrcType)) }; 1296 1297 Result parse(Input!SrcType input, in Caller caller) 1298 { 1299 auto skipped = skip.build!(ParserKind!(false, kind.hasError), SrcType).parse(input.save, caller); 1300 1301 if(skipped.match) 1302 { 1303 return parser.build!(kind, SrcType).parse(skipped.nextInput, caller); 1304 } 1305 else 1306 { 1307 return parser.build!(kind, SrcType).parse(input, caller); 1308 } 1309 } 1310 } 1311 } 1312 1313 debug(ctpg) unittest 1314 { 1315 foreach(conv; convs) foreach(kind; ParserKinds) 1316 { 1317 with(parse!(skip!(parsers.string_!"\n", parsers.string_!"hoge"), kind)(conv!"hogehoge")) 1318 { 1319 assert(match == true); 1320 static if(kind.hasValue) assert(value == "hoge"); 1321 assert(nextInput.source == conv!"hoge"); 1322 } 1323 1324 with(parse!(skip!(parsers.string_!"\n\n", parsers.string_!"hoge"), kind)(conv!"\n\nhogehoge")) 1325 { 1326 assert(match == true); 1327 static if(kind.hasValue) assert(value == "hoge"); 1328 assert(nextInput.source == conv!"hoge"); 1329 } 1330 } 1331 }