1 module pry.combinators; 2 3 import pry.traits; 4 import std.meta, std.range.primitives, std.typecons, std.traits; 5 6 private: 7 8 public struct Nullable(T){ 9 bool isNull = true; 10 T value; 11 12 this(T val){ 13 value = val; 14 isNull = false; 15 } 16 17 void opAssign(T value){ 18 this.value = value; 19 isNull = false; 20 } 21 22 void nullify(){ 23 isNull = true; 24 static if(is(T == struct)) 25 value.tupleof = T.init.tupleof; 26 else 27 value = T.init; 28 } 29 30 alias value this; 31 } 32 33 struct RepImpl(bool collect, Array, size_t minTimes, size_t maxTimes, Parser){ 34 alias Stream = ParserStream!Parser; 35 static if(collect) 36 alias Value = Array; 37 else 38 alias Value = Stream.Range; 39 private Parser parser; 40 41 bool parse(ref Stream stream, ref Value value, ref Stream.Error err) const { 42 auto start = stream.mark; 43 ParserValue!Parser tmp; 44 size_t i = 0; 45 static if(collect) value = null; 46 for(; i<minTimes; i++) { 47 if(!parser.parse(stream, tmp, err)){ 48 stream.restore(start); 49 return false; 50 } 51 static if(collect) value ~= tmp; 52 } 53 for(; i<maxTimes; i++){ 54 if(!parser.parse(stream, tmp, err)) break; 55 static if(collect) value ~= tmp; 56 } 57 static if(!collect) 58 value = stream.slice(start); 59 return true; 60 } 61 } 62 63 64 /// Apply parser for minTimes times or more and return consumed range. 65 public auto rep(size_t minTimes=1, size_t maxTimes=size_t.max, Parser)(Parser parser) 66 if(isParser!Parser){ 67 return RepImpl!(false, ParserValue!Parser[], minTimes, maxTimes, Parser)(parser); 68 } 69 70 /// Apply parser for minTimes times or more and return array of results. 71 public auto array(size_t minTimes=1, size_t maxTimes=size_t.max, Parser)(Parser parser) 72 if(isParser!Parser){ 73 return RepImpl!(true, ParserValue!Parser[], minTimes, maxTimes, Parser)(parser); 74 } 75 76 /// Apply parser for minTimes times or more and encode results to an UTF string. 77 public auto utfString(Char, size_t minTimes=1, size_t maxTimes=size_t.max, Parser)(Parser parser) 78 if(isParser!Parser){ 79 return RepImpl!(true, immutable(Char)[], minTimes, maxTimes, Parser)(parser); 80 } 81 82 unittest 83 { 84 import pry.atoms, pry.stream; 85 alias S = SimpleStream!string; 86 with(parsers!S) 87 { 88 auto p = tk!'a'.rep!2; 89 string r; 90 S.Error err; 91 auto s = S("aaac"); 92 assert(p.parse(s, r, err)); 93 assert(r == "aaa"); 94 95 s = S("a"); 96 assert(!p.parse(s, r, err)); 97 assert(err.location == 1); 98 assert(err.reason == "unexpected end of stream"); 99 100 auto p2 = range!('a', 'b').array; 101 dchar[] r2; 102 s = S("aba"); 103 assert(p2.parse(s, r2, err)); 104 assert(r2 == "aba"d); 105 assert(s.empty); 106 107 108 s = S("aaaa"); 109 auto p3 = tk!'a'.rep!(2,3); 110 assert(p3.parse(s, r, err)); 111 assert(r == "aaa"); 112 assert(!s.empty); 113 114 s = S("ФЯ"); 115 auto p4 = any(tk!'Ф', tk!'Я').utfString!char; 116 assert(p4.parse(s, r, err)); 117 assert(r == "ФЯ"); 118 assert(s.empty); 119 } 120 } 121 122 123 struct Map(Parser, alias f) { 124 alias Stream = ParserStream!Parser; 125 alias Value = typeof(f(ParserValue!Parser.init)); 126 alias mapper = f; 127 Parser parser; 128 129 bool parse(ref Stream stream, ref Value value, ref Stream.Error err) const { 130 ParserValue!Parser tmp; 131 if(parser.parse(stream, tmp, err)){ 132 value = f(tmp); 133 return true; 134 } 135 else 136 return false; 137 } 138 } 139 140 /// Apply a mapping function to the value of parser. 141 public template map(alias f) 142 { 143 auto map(Parser)(Parser parser) 144 if(isParser!Parser){ 145 return Map!(Parser, f)(parser); 146 } 147 } 148 149 /// 150 unittest { 151 import std.conv; 152 import pry.atoms, pry.stream; 153 alias S = SimpleStream!string; 154 with(parsers!S) { 155 auto digits = range!('0', '9').rep.map!(x=>x.to!int); 156 S s = S("90"); 157 int r; 158 S.Error err; 159 assert(digits.parse(s, r, err)); 160 assert(r == 90); 161 s = S("a"); 162 assert(!digits.parse(s, r, err)); 163 assert(err.location == 0); 164 } 165 } 166 167 struct Seq(P...){ 168 alias Stream = ParserStream!(P[0]); 169 alias Unfiltered = staticMap!(ParserValue, P); 170 alias Values = EraseAll!(Nothing, Unfiltered); 171 enum mapIndex(size_t index) = (){ 172 size_t j = 0; 173 foreach(i, Type; Unfiltered[0..index]){ 174 static if(!is(Type == Nothing)){ 175 j++; 176 } 177 } 178 return j; 179 }(); 180 P parsers; 181 182 bool parse(ref Stream stream, ref Tuple!Values value, ref Stream.Error err) const { 183 Nothing nothing; 184 auto save = stream.mark; 185 foreach(i, ref p; parsers) { 186 static if(is(ParserValue!(typeof(p)) == Nothing)) { 187 if(!p.parse(stream, nothing, err)){ 188 stream.restore(save); 189 return false; 190 } 191 } 192 else { 193 enum j = mapIndex!i; 194 if(!p.parse(stream, value[j], err)){ 195 stream.restore(save); 196 return false; 197 } 198 } 199 } 200 return true; 201 } 202 } 203 204 /// Apply multiple parsers one after another as a sequence. 205 public auto seq(P...)(P parsers) 206 if(allSatisfy!(isParser, P)){ 207 static if(P.length == 0) 208 return Nothing(); 209 else 210 return Seq!P(parsers); 211 } 212 213 /// 214 unittest { 215 import pry.atoms, pry.stream; 216 import std.range.primitives, std.typecons; 217 alias S = SimpleStream!string; 218 with(parsers!S) { 219 auto elements = seq(tk!'a', range!('a', 'd'), tk!'c'); 220 S s = S("abc"); 221 Tuple!(dchar, dchar, dchar) val; 222 S.Error err; 223 assert(elements.parse(s, val, err)); 224 assert(s.empty); 225 assert(val == tuple('a', 'b', 'c')); 226 s = S("axc"); 227 assert(!elements.parse(s, val, err)); 228 assert(s.front == 'a'); 229 assert(err.location == 1); 230 } 231 } 232 233 /// 234 unittest { 235 import pry.atoms, pry.stream; 236 alias S = SimpleStream!string; 237 with(parsers!S) { 238 auto p = seq(tk!'a', tk!'b', eof); 239 auto s1 = "abc".stream; 240 auto s2 = "ab".stream; 241 S.Error err; 242 Tuple!(dchar, dchar) val; 243 assert(!p.parse(s1, val, err)); 244 assert(p.parse(s2, val, err)); 245 } 246 } 247 248 struct TList(T...){} 249 250 template commonPrefixLength(T1, T2){ 251 static if(is(T1 == TList!U1, U1...)){ 252 static if(is(T2 == TList!U2, U2...)){ 253 static if(U1.length == 0 || U2.length == 0){ 254 enum commonPrefixLength = 0; 255 } 256 else static if(is(Unqual!(U1[0]) == Unqual!(U2[0]))){ 257 enum commonPrefixLength = 1 + 258 commonPrefixLength!(TList!(U1[1..$]), TList!(U2[1..$])); 259 } 260 else { 261 enum commonPrefixLength = 0; 262 } 263 } 264 } 265 } 266 267 auto commonPrefix(P1, P2)(P1 p1, P2 p2){ 268 static if(is(P1 : Seq!U1, U1...)){ 269 enum isSeq = true; 270 alias T1 = TList!U1; 271 } 272 else{ 273 enum isSeq = false; 274 alias T1 = TList!P1; 275 } 276 static if(is(P2 : Seq!U2, U2...)){ 277 alias T2 = TList!U2; 278 } 279 else{ 280 alias T2 = TList!P2; 281 } 282 enum len = commonPrefixLength!(T1,T2); 283 static if(isSeq) 284 return seq(p1.parsers[0..len]); 285 else static if(len) 286 return seq(p1); 287 else 288 return Nothing(); 289 } 290 291 auto commonPrefix(P...)(P p) 292 if(P.length > 2){ 293 return commonPrefix(commonPrefix(p[0], p[1]), p[2..$]); 294 } 295 /* 296 unittest { 297 import pry.atoms, pry.stream; 298 alias S = SimpleStream!string; 299 with(parsers!S){ 300 auto p1 = seq(tk!'a', tk!'b'); 301 auto p2 = seq(tk!'a'); 302 auto p3 = tk!'a'; 303 auto p4 = seq(tk!'b', tk!'a'); 304 assert(commonPrefix(p1, p2) == seq(tk!'a')); 305 assert(commonPrefix(p2, p3) == seq(tk!'a')); 306 assert(commonPrefix(p3, p3) == seq(tk!'a')); 307 assert(commonPrefix(p3, p1) == seq(tk!'a')); 308 assert(commonPrefix(p1, p4) == Nothing()); 309 assert(commonPrefix(p3, p4) == Nothing()); 310 assert(commonPrefix(p4, p3) == Nothing()); 311 assert(commonPrefix(p1, p2, p3) == seq(tk!'a')); 312 assert(commonPrefix(p1, p2, p3, p4) == Nothing()); 313 } 314 } 315 */ 316 auto suffix(P1, P2)(P1 prefix, P2 parser) 317 if(isParser!P1 && isParser!P2){ 318 static if(is(P1 : Seq!U1, U1...)){ 319 alias T1 = TList!U1; 320 } 321 else{ 322 alias T1 = TList!P1; 323 } 324 static if(is(P2 : Seq!U2, U2...)){ 325 enum isSeq = true; 326 alias T2 = TList!U2; 327 } 328 else{ 329 enum isSeq = false; 330 alias T2 = TList!P2; 331 } 332 enum len = commonPrefixLength!(T1,T2); 333 static if(is(T1 : TList!U, U...)){ 334 static assert(len == U.length); 335 } 336 static if(isSeq){ 337 return seq(parser.parsers[len..$]); 338 } 339 else{ 340 static if(len > 0) 341 return Nothing(); 342 else 343 return seq(parser); 344 } 345 } 346 /* 347 unittest{ 348 import pry.atoms, pry.stream; 349 alias S = SimpleStream!string; 350 with(parsers!S){ 351 auto p1 = seq(tk!'a', tk!'b'); 352 auto p2 = seq(tk!'a'); 353 auto p3 = tk!'a'; 354 auto p4 = seq(tk!'b', tk!'a'); 355 assert(suffix(p2, p1) == seq(tk!'b')); 356 assert(suffix(p3, p1) == seq(tk!'b')); 357 assert(suffix(p2, p3) == Nothing()); 358 } 359 }*/ 360 361 template Unmap(P){ 362 static if(is(P : Map!(U, f), alias f, U)){ 363 alias Unmap = U; 364 } 365 else static if(is(P : const(Map!(U, f)), alias f, U)){ 366 alias Unmap = const(U); 367 } 368 else{ 369 alias Unmap = P; 370 } 371 } 372 373 auto unmap(P)(P parser){ 374 static if(is(P : Map!(U, f), alias f, U)){ 375 return parser.parser; 376 } 377 else { 378 return parser; 379 } 380 } 381 382 unittest{ 383 import pry.atoms, pry.stream; 384 alias S = SimpleStream!string; 385 with(parsers!S){ 386 auto x = tk!'a'.map!(x => 1); 387 assert(unmap(x) == tk!'a'); 388 assert(unmap(unmap(x)) == tk!'a'); 389 } 390 } 391 392 struct Any(P...){ 393 import std.variant, std.typecons; 394 alias Stream = ParserStream!(P[0]); 395 alias Values = NoDuplicates!(staticMap!(ParserValue, P)); 396 397 static if(Values.length == 1) 398 alias Value = Values[0]; 399 else 400 alias Value = Algebraic!Values; 401 402 P parsers; 403 Values value; 404 405 this(P parsers){ 406 this.parsers = parsers; 407 } 408 409 bool parse(ref Stream stream, ref Value value, ref Stream.Error err) const { 410 Stream.Error current, deepest; 411 bool ret = false; 412 foreach(i, ref p; parsers) { 413 ParserValue!(typeof(p)) suffixValue; 414 static if(i == 0){ 415 if(p.parse(stream, suffixValue, deepest)){ 416 value = suffixValue; 417 return true; 418 } 419 } 420 else { 421 if(p.parse(stream, suffixValue, current)){ 422 value = suffixValue; 423 return true; 424 } 425 // pick the deeper error 426 if(deepest.location < current.location){ 427 deepest = current; 428 } 429 } 430 } 431 L_end: 432 err = deepest; 433 return ret; 434 } 435 } 436 437 /// Try each of provided parsers until one succeeds. 438 public auto any(P...)(P parsers) 439 if(allSatisfy!(isParser, P)){ 440 return Any!P(parsers); 441 } 442 443 /// 444 unittest { 445 import pry.atoms, pry.stream; 446 import std.range.primitives, std.conv, std.variant; 447 alias S = SimpleStream!string; 448 with(parsers!S) { 449 auto digits = range!('0', '9').rep.map!(x => x.to!int); 450 static assert(isParser!(typeof(digits))); 451 auto parser = any(tk!'a', digits); 452 S s = "10a"; 453 S.Error err; 454 Algebraic!(dchar, int) value; 455 assert(parser.parse(s, value, err)); 456 assert(value == 10); 457 assert(parser.parse(s, value, err)); 458 assert(value == cast(dchar)'a'); 459 assert(s.empty); 460 } 461 } 462 463 unittest { 464 import pry.atoms, pry.stream; 465 alias S = SimpleStream!string; 466 with(parsers!S) { 467 auto e = dynamic!int; 468 e = any( 469 seq(tk!'0', e).map!(x => 1), 470 tk!'1'.map!(x => 0) 471 ); 472 S.Error err; 473 int val; 474 S s = S("0001"); 475 assert(e.parse(s, val, err)); 476 } 477 } 478 479 unittest { 480 import pry.atoms, pry.stream; 481 import std.typecons; 482 alias S = SimpleStream!string; 483 with(parsers!S) { 484 auto p = any( 485 seq(tk!'0', tk!'0'), 486 seq(tk!'1', tk!'1') 487 ); 488 S s = "01".stream; 489 Tuple!(dchar, dchar) value; 490 S.Error err; 491 assert(!p.parse(s, value, err)); 492 assert(err.location == 1); 493 } 494 } 495 496 497 struct SkipWs(P) { 498 private P parser; 499 alias Stream = ParserStream!P; 500 alias Value = ParserValue!P; 501 502 bool parse(ref Stream s, ref Value v, ref Stream.Error err) const { 503 import std.uni; 504 while(!s.empty && isWhite(s.front)) s.popFront(); 505 return parser.parse(s, v, err); 506 } 507 } 508 509 /// Skip whitespace at front then apply the `parser`. 510 public auto skipWs(P)(P parser) 511 if(isParser!P && is(ElementType!(ParserStream!P) : dchar)){ 512 return SkipWs!P(parser); 513 } 514 515 /// 516 unittest { 517 import pry.atoms, pry.stream; 518 alias S = SimpleStream!string; 519 with(parsers!S) { 520 auto normal = range!('0', '9').rep; 521 auto skipping = range!('0', '9').skipWs.rep; 522 auto s1 = "0 9 1".stream; 523 string r; 524 S.Error err; 525 assert(skipping.parse(s1, r, err)); 526 assert(s1.empty); 527 assert(r == "0 9 1"); 528 529 auto s2 = "0 9 1".stream; 530 assert(normal.parse(s2, r, err)); 531 assert(!s2.empty); 532 assert(r == "0"); 533 assert(s2.front == ' '); 534 } 535 } 536 537 struct AAImpl(size_t minTimes, size_t maxTimes, P) { 538 private P parser; 539 alias Stream = ParserStream!P; 540 alias Pair = ParserValue!P; 541 alias Key = typeof(Pair.init[0]); 542 alias Value = typeof(Pair.init[1]); 543 544 bool parse(ref Stream stream, ref Value[Key] aa, ref Stream.Error err) const { 545 auto start = stream.mark; 546 Pair tmp; 547 size_t i = 0; 548 for(; i<minTimes; i++) { 549 if(!parser.parse(stream, tmp, err)){ 550 stream.restore(start); 551 return false; 552 } 553 aa[tmp[0]] = tmp[1]; 554 } 555 for(; i<maxTimes; i++){ 556 if(!parser.parse(stream, tmp, err)) break; 557 aa[tmp[0]] = tmp[1]; 558 } 559 return true; 560 } 561 } 562 563 /++ 564 Apply parser of key-value pairs (2-tuples) for minTimes times or more up to maxTimes, 565 construct an AA out of the results of parsing. 566 +/ 567 public auto aa(size_t minTimes=1, size_t maxTimes=size_t.max, P)(P parser) 568 if(isParser!P && isTuple!(ParserValue!P) && ParserValue!P.length == 2){ 569 return AAImpl!(minTimes, maxTimes, P)(parser); 570 } 571 572 /// 573 unittest { 574 import pry.atoms, pry.stream; 575 import std.conv; 576 alias S = SimpleStream!string; 577 with(parsers!S) { 578 auto p = seq(range!('a', 'z').rep.skipWs, stk!'=', range!('0', '9').rep.skipWs) 579 .map!(x => tuple(x[0], to!int(x[2]))).aa; 580 auto s = "a = 1 b = 3 temp = 36".stream; 581 int[string] table; 582 S.Error err; 583 assert(p.parse(s, table, err)); 584 assert(s.empty); 585 assert(table["a"] == 1); 586 assert(table["b"] == 3); 587 assert(table["temp"] == 36); 588 } 589 } 590 591 struct Optional(P) { 592 private P parser; 593 alias Stream = ParserStream!P; 594 alias Value = ParserValue!P; 595 596 bool parse(ref Stream stream, ref Nullable!Value option, ref Stream.Error err) const { 597 Value tmp; 598 if(parser.parse(stream, tmp, err)) { 599 option = tmp; 600 return true; 601 } 602 option.nullify(); 603 return true; 604 } 605 } 606 607 /// Try to apply `parser`, produce null on failure. 608 public auto optional(P)(P parser) 609 if(isParser!P){ 610 return Optional!P(parser); 611 } 612 613 /// 614 unittest { 615 import pry.atoms, pry.stream; 616 alias S = SimpleStream!string; 617 with(parsers!S){ 618 auto p = range!('a', 'z').optional; 619 auto s = "a".stream; 620 Nullable!dchar c; 621 S.Error err; 622 assert(p.parse(s, c, err)); 623 assert(c == 'a'); 624 assert(s.empty); 625 assert(p.parse(s, c, err)); 626 assert(c.isNull); 627 } 628 } 629 630 struct Slice(P){ 631 private P parser; 632 alias Stream = ParserStream!P; 633 alias Value = ParserValue!P; 634 635 bool parse(ref Stream stream, ref Stream.Range range, ref Stream.Error err) const { 636 auto start = stream.mark(); 637 Value val; 638 if(parser.parse(stream, val, err)) { 639 range = stream.slice(start); 640 return true; 641 } 642 return false; 643 } 644 } 645 646 /// Apply parser, discard the value and instead produce the slice of parsed stream. 647 public auto slice(P)(P parser) 648 if(isParser!P){ 649 return Slice!P(parser); 650 } 651 652 /// 653 unittest { 654 import pry.atoms, pry.stream, std.conv; 655 alias S = SimpleStream!string; 656 with(parsers!S){ 657 auto p = seq(tk!'-'.optional, range!('0', '9').rep) 658 .slice 659 .map!(x => to!int(x)); 660 auto s = "-13".stream; 661 int value; 662 S.Error err; 663 assert(p.parse(s, value, err)); 664 assert(value == -13); 665 s = "5".stream; 666 assert(p.parse(s, value, err)); 667 assert(value == 5); 668 } 669 } 670 671 struct Delimited(Item, Delimiter){ 672 private Item item; 673 private Delimiter delimiter; 674 alias Stream = ParserStream!Item; 675 alias Value = ParserValue!Item; 676 677 bool parse(ref Stream stream, ref Value[] values, ref Stream.Error err) const { 678 import std.array; 679 values = Value[].init; 680 bool first = true; 681 Value val; 682 ParserValue!Delimiter tmp; 683 auto app = appender!(Value[]); 684 auto m = stream.mark(); 685 if (item.parse(stream, val, err)){ 686 app.put(val); 687 for(;;) { 688 if(!delimiter.parse(stream, tmp, err)) break; 689 if (item.parse(stream, val, err)) 690 app.put(val); 691 else { 692 stream.restore(m); 693 return false; 694 } 695 } 696 } 697 values = app.data(); 698 return true; 699 } 700 } 701 702 /// Parse a sequence of `item` delimited by `del` 703 public auto delimited(Item, Delimiter)(Item item, Delimiter del) 704 if(isParser!Item && isParser!Delimiter){ 705 return Delimited!(Item, Delimiter)(item, del); 706 } 707 708 /// 709 unittest{ 710 import pry.atoms, pry.stream, std.conv; 711 alias S = SimpleStream!string; 712 with(parsers!S){ 713 auto p = delimited( 714 range!('0', '9').rep.skipWs.map!(x => to!int(x)), 715 stk!',' 716 ); 717 auto s = " 2, 4,5".stream; 718 int[] values; 719 S.Error err; 720 assert(p.parse(s, values, err)); 721 assert(values == [2, 4, 5]); 722 s = "10".stream; 723 assert(p.parse(s, values, err)); 724 assert(values == [10]); 725 s = "".stream; 726 assert(p.parse(s, values, err)); 727 assert(values == []); 728 } 729 } 730 731 struct Lookahead(Parser) { 732 Parser parser; 733 alias Stream = ParserStream!Parser; 734 alias Value = ParserValue!Parser; 735 736 bool parse(ref Stream stream, ref Nothing _, ref Stream.Error err) const { 737 auto m = stream.mark(); 738 Value val; 739 bool r = parser.parse(stream, val, err); 740 stream.restore(m); 741 return r; 742 } 743 } 744 745 /// Use Parser as lookahead, doesn't consume input stream. 746 auto lookahead(Parser)(Parser p) 747 if(isParser!Parser){ 748 return Lookahead!Parser(p); 749 } 750 751 struct NegLookahead(Parser) { 752 Parser parser; 753 alias Stream = ParserStream!Parser; 754 alias Value = ParserValue!Parser; 755 756 bool parse(ref Stream stream, ref Nothing _, ref Stream.Error err) const { 757 auto m = stream.mark(); 758 Value val; 759 bool r = !parser.parse(stream, val, err); 760 stream.restore(m); 761 return r; 762 } 763 } 764 765 /// Use Parser as lookahead, doesn't consume input stream. 766 auto negLookahead(Parser)(Parser p) 767 if(isParser!Parser){ 768 return NegLookahead!Parser(p); 769 } 770 771 unittest { 772 import pry.atoms, pry.stream, std.conv; 773 alias S = SimpleStream!string; 774 with(parsers!S){ 775 auto p1 = seq(tk!'a', tk!'b'.lookahead); 776 auto s1 = "ab".stream; 777 Tuple!dchar val; 778 S.Error err; 779 assert(p1.parse(s1, val, err)); 780 assert(val[0] == 'a'); 781 auto p2 = seq(tk!'a', tk!'b'.negLookahead); 782 s1 = "ab".stream; 783 assert(!p2.parse(s1, val, err)); 784 auto s2 = "ac".stream; 785 assert(p2.parse(s2, val, err)); 786 } 787 }