github.com/jmigpin/editor@v1.6.0/util/parseutil/lrparser/lrparser_test.go (about) 1 package lrparser 2 3 import ( 4 "flag" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/token" 9 "os" 10 "os/exec" 11 "strings" 12 "testing" 13 14 "github.com/jmigpin/editor/util/astut" 15 "github.com/jmigpin/editor/util/testutil" 16 "golang.org/x/tools/go/ast/astutil" 17 ) 18 19 func TestLrparser1(t *testing.T) { 20 gram := ` 21 ^S = C C; 22 C = "c" C | "d"; 23 ` 24 in := "●ccdd" 25 out := ` 26 -> ^S: "ccdd" 27 -> C: "ccd" 28 -> "c": "c" 29 -> C: "cd" 30 -> "c": "c" 31 -> C: "d" 32 -> "d": "d" 33 -> C: "d" 34 -> "d": "d" 35 ` 36 testLrparserMode1(t, gram, in, out) 37 } 38 func TestLrparser2(t *testing.T) { 39 gram := ` 40 ^id = "a" id | "a"; 41 ` 42 in := "●aaa" 43 out := ` 44 -> ^id: "aaa" 45 -> "a": "a" 46 -> ^id: "aa" 47 -> "a": "a" 48 -> ^id: "a" 49 -> "a": "a" 50 ` 51 testLrparserMode1(t, gram, in, out) 52 } 53 func TestLrparser3(t *testing.T) { 54 gram := ` 55 ^id = id "a" | "a"; 56 ` 57 in := "●aaa" 58 out := ` 59 -> ^id: "aaa" 60 -> ^id: "aa" 61 -> ^id: "a" 62 -> "a": "a" 63 -> "a": "a" 64 -> "a": "a" 65 ` 66 testLrparserMode1(t, gram, in, out) 67 } 68 func TestLrparser4(t *testing.T) { 69 gram := ` 70 ^id = (digit)?; 71 ` 72 in := "●1" 73 out := ` 74 -> ^id: "1" 75 -> (digit)?: "1" 76 -> digit: "1" 77 ` 78 testLrparserMode1(t, gram, in, out) 79 } 80 func TestLrparser5(t *testing.T) { 81 gram := ` 82 #^id = letter id2 letter; 83 #id2 = digit | nil; 84 ^id = letter (digit)? letter; 85 ` 86 in := "●aa" 87 out := ` 88 -> ^id: "aa" 89 -> letter: "a" 90 -> (digit)?: "" 91 -> letter: "a" 92 ` 93 testLrparserMode1(t, gram, in, out) 94 } 95 func TestLrparser6(t *testing.T) { 96 gram := ` 97 ^id = letter (letter|digit)* digit; 98 ` 99 in := "●a11" 100 out := ` 101 -> ^id: "a11" 102 -> letter: "a" 103 -> ([letter|digit])*: "1" 104 -> ([letter|digit])*: "" 105 -> [letter|digit]: "1" 106 -> digit: "1" 107 -> digit: "1" 108 ` 109 testLrparserMode1(t, gram, in, out) 110 } 111 func TestLrparser7(t *testing.T) { 112 gram := ` 113 ^id = (letter|digit)*; 114 ` 115 in := "●a1" 116 out := ` 117 -> ^id: "a1" 118 -> ([letter|digit])*: "a1" 119 -> ([letter|digit])*: "a" 120 -> ([letter|digit])*: "" 121 -> [letter|digit]: "a" 122 -> letter: "a" 123 -> [letter|digit]: "1" 124 -> digit: "1" 125 ` 126 testLrparserMode1(t, gram, in, out) 127 } 128 func TestLrparser7b(t *testing.T) { 129 gram := ` 130 ^id = (letter|digit)+; 131 ` 132 in := "●a1" 133 out := ` 134 -> ^id: "a1" 135 -> ([letter|digit])+: "a1" 136 -> ([letter|digit])*: "a" 137 -> ([letter|digit])*: "" 138 -> [letter|digit]: "a" 139 -> letter: "a" 140 -> [letter|digit]: "1" 141 -> digit: "1" 142 ` 143 testLrparserMode1(t, gram, in, out) 144 } 145 func TestLrparser8(t *testing.T) { 146 gram := ` 147 #^S = "a" ("a"|"1")*; 148 149 ^S = "a" s2; 150 s2 = "a" s2 | "1" s2 | nil; 151 ` 152 in := "●aa1" 153 out := ` 154 -> ^S: "aa1" 155 -> "a": "a" 156 -> s2: "a1" 157 -> "a": "a" 158 -> s2: "1" 159 -> "1": "1" 160 -> s2: "" 161 ` 162 testLrparserMode1(t, gram, in, out) 163 } 164 func TestLrparser9a(t *testing.T) { 165 gram := ` 166 ^S = letter (letter)*; 167 ` 168 in := "●aaa" 169 out := ` 170 -> ^S: "aaa" 171 -> letter: "a" 172 -> (letter)*: "aa" 173 -> (letter)*: "a" 174 -> (letter)*: "" 175 -> letter: "a" 176 -> letter: "a" 177 ` 178 testLrparserMode1(t, gram, in, out) 179 } 180 func TestLrparser9b(t *testing.T) { 181 gram := ` 182 ^S = letter (letter)*; 183 ` 184 in := "●a" 185 out := ` 186 -> ^S: "a" 187 -> letter: "a" 188 -> (letter)*: "" 189 ` 190 testLrparserMode1(t, gram, in, out) 191 } 192 func TestLrparser9c(t *testing.T) { 193 gram := ` 194 ^S = letter (letter)+; 195 ` 196 in := "●aaaa" 197 out := ` 198 -> ^S: "aaaa" 199 -> letter: "a" 200 -> (letter)+: "aaa" 201 -> (letter)*: "aa" 202 -> (letter)*: "a" 203 -> (letter)*: "" 204 -> letter: "a" 205 -> letter: "a" 206 -> letter: "a" 207 ` 208 testLrparserMode1(t, gram, in, out) 209 } 210 func TestLrparser10(t *testing.T) { 211 gram := ` 212 ^S = (letter|digit)?; 213 ` 214 in := "●1" 215 out := ` 216 -> ^S: "1" 217 -> ([letter|digit])?: "1" 218 -> [letter|digit]: "1" 219 -> digit: "1" 220 ` 221 testLrparserMode1(t, gram, in, out) 222 } 223 func TestLrparser11(t *testing.T) { 224 gram := ` 225 ^S = (letter digit)+; 226 ` 227 in := "●a1b2c3" 228 out := ` 229 -> ^S: "a1b2c3" 230 -> ([letter digit])+: "a1b2c3" 231 -> ([letter digit])*: "a1b2" 232 -> ([letter digit])*: "a1" 233 -> ([letter digit])*: "" 234 -> [letter digit]: "a1" 235 -> letter: "a" 236 -> digit: "1" 237 -> [letter digit]: "b2" 238 -> letter: "b" 239 -> digit: "2" 240 -> [letter digit]: "c3" 241 -> letter: "c" 242 -> digit: "3" 243 ` 244 testLrparserMode1(t, gram, in, out) 245 } 246 func TestLrparser11b(t *testing.T) { 247 gram := ` 248 ^S = (letter digit)*; 249 ` 250 in := "●a1b2c3" 251 out := ` 252 -> ^S: "a1b2c3" 253 -> ([letter digit])*: "a1b2c3" 254 -> ([letter digit])*: "a1b2" 255 -> ([letter digit])*: "a1" 256 -> ([letter digit])*: "" 257 -> [letter digit]: "a1" 258 -> letter: "a" 259 -> digit: "1" 260 -> [letter digit]: "b2" 261 -> letter: "b" 262 -> digit: "2" 263 -> [letter digit]: "c3" 264 -> letter: "c" 265 -> digit: "3" 266 ` 267 testLrparserMode1(t, gram, in, out) 268 } 269 func TestLrparser11c(t *testing.T) { 270 gram := ` 271 ^S = letter digit; 272 ` 273 in := "●a1" 274 out := ` 275 -> ^S: "a1" 276 -> letter: "a" 277 -> digit: "1" 278 ` 279 testLrparserMode1(t, gram, in, out) 280 } 281 func TestLrparser12(t *testing.T) { 282 gram := ` 283 ^S = ((":\"'")%)+; 284 ` 285 in := "●:\":" 286 out := ` 287 -> ^S: ":\":" 288 -> (":\"'"%)+: ":\":" 289 -> (":\"'"%)*: ":\"" 290 -> (":\"'"%)*: ":" 291 -> (":\"'"%)*: "" 292 -> ":\"'"%: ":" 293 -> ":\"'"%: "\"" 294 -> ":\"'"%: ":" 295 ` 296 testLrparserMode1(t, gram, in, out) 297 } 298 func TestLrparser13(t *testing.T) { 299 gram := ` 300 ^S = letter (letter|digit)* digit; 301 ` 302 in := "●aa11" 303 out := ` 304 -> ^S: "aa11" 305 -> letter: "a" 306 -> ([letter|digit])*: "a1" 307 -> ([letter|digit])*: "a" 308 -> ([letter|digit])*: "" 309 -> [letter|digit]: "a" 310 -> letter: "a" 311 -> [letter|digit]: "1" 312 -> digit: "1" 313 -> digit: "1" 314 ` 315 testLrparserMode1(t, gram, in, out) 316 } 317 func TestLrparser14(t *testing.T) { 318 gram := ` 319 // reduce/reduce conflict 320 ^S = (s2)+; 321 s2 = (letter)+; 322 ` 323 in := "●aa" 324 out := ` 325 ` 326 // shift/reduce conflict: either make 327 // - several s2, each with one letter 328 // - or one s2 with many letters 329 //testLrparserMode1(t, gram, in, out) // conflict 330 331 // use shift by default 332 _, err := testLrparserMode3(t, gram, in, out, false, false, true) 333 if err == nil { 334 t.Fatal("expecting error") 335 } 336 if !strings.Contains(err.Error(), "conflict") { 337 t.Fatal("expecting conflict error") 338 } 339 } 340 func TestLrparser15(t *testing.T) { 341 gram := ` 342 ^S = (s2)~ s3; 343 s2 = "abc"; 344 s3 = "de"; 345 ` 346 in := "ab●cde" 347 out := ` 348 -> ^S: "cde" 349 -> "abc"~: "c" 350 -> s3: "de" 351 -> "de": "de" 352 ` 353 testLrparserMode2(t, gram, in, out, false, false, false) 354 } 355 func TestLrparser16(t *testing.T) { 356 gram := ` 357 ^S = (s2)~ ((s2)%)+; 358 s2 = "abc"; 359 ` 360 in := "ab●caa" 361 out := ` 362 -> ^S: "caa" 363 -> "abc"~: "c" 364 -> ("abc"%)+: "aa" 365 -> ("abc"%)*: "a" 366 -> ("abc"%)*: "" 367 -> "abc"%: "a" 368 -> "abc"%: "a" 369 ` 370 testLrparserMode2(t, gram, in, out, false, false, false) 371 } 372 func TestLrparser17(t *testing.T) { 373 gram := ` 374 ^S = ("+")! "+"; 375 ` 376 in := "●0+" 377 out := ` 378 -> ^S: "0+" 379 -> "+"!: "0" 380 -> "+": "+" 381 ` 382 testLrparserMode1(t, gram, in, out) 383 } 384 func TestLrparser18(t *testing.T) { 385 gram := ` 386 ^S = (s2)! "+"; 387 s2 = ("abc")%|("defg")%; 388 //s2 = ("abc"|"defg")%; 389 ` 390 in := "●h+" 391 out := ` 392 -> ^S: "h+" 393 -> "abcdefg"!: "h" 394 -> "+": "+" 395 ` 396 testLrparserMode1(t, gram, in, out) 397 } 398 func TestLrparser19(t *testing.T) { 399 gram := ` 400 ^S = (@dropRunes((s2)%,("b")%))+; 401 s2 = "abc"; 402 ` 403 in := "●ac" 404 out := ` 405 -> ^S: "ac" 406 -> ("ac"%)+: "ac" 407 -> ("ac"%)*: "a" 408 -> ("ac"%)*: "" 409 -> "ac"%: "a" 410 -> "ac"%: "c" 411 ` 412 testLrparserMode1(t, gram, in, out) 413 } 414 func TestLrparser20(t *testing.T) { 415 gram := ` 416 ^S = (s2|s3|s4)+; 417 s2 = "ab"; 418 s3 = "c"; 419 s4 = "d"; 420 ` 421 in := "●ababdabcd" 422 out := ` 423 -> ^S: "ababdabcd" 424 -> ([s2|s3|s4])+: "ababdabcd" 425 -> ([s2|s3|s4])*: "ababdabc" 426 -> ([s2|s3|s4])*: "ababdab" 427 -> ([s2|s3|s4])*: "ababd" 428 -> ([s2|s3|s4])*: "abab" 429 -> ([s2|s3|s4])*: "ab" 430 -> ([s2|s3|s4])*: "" 431 -> [s2|s3|s4]: "ab" 432 -> s2: "ab" 433 -> "ab": "ab" 434 -> [s2|s3|s4]: "ab" 435 -> s2: "ab" 436 -> "ab": "ab" 437 -> [s2|s3|s4]: "d" 438 -> s4: "d" 439 -> "d": "d" 440 -> [s2|s3|s4]: "ab" 441 -> s2: "ab" 442 -> "ab": "ab" 443 -> [s2|s3|s4]: "c" 444 -> s3: "c" 445 -> "c": "c" 446 -> [s2|s3|s4]: "d" 447 -> s4: "d" 448 -> "d": "d" 449 ` 450 testLrparserMode1(t, gram, in, out) 451 } 452 func TestLrparser21(t *testing.T) { 453 gram := ` 454 //^S = (sep)* arg args2; 455 //args2 = (sep)+ arg args2 | (sep)+ | nil; // ok 456 //args2 = (sep)+ arg args2 | (sep)*; // ok (was conflict) 457 458 ^S = (sep)* arg ((sep)+ arg)* (sep)*; // ok 459 460 //^S = (sep)* arg ((sep)+ arg (sep)*)* ; 461 //^S = ((sep)* arg)+ (sep)*; 462 sep = " "; 463 arg = "a"; 464 ` 465 in := "● a a " 466 out := ` 467 -> ^S: " a a " 468 -> (sep)*: " " 469 -> (sep)*: " " 470 -> (sep)*: "" 471 -> sep: " " 472 -> " ": " " 473 -> sep: " " 474 -> " ": " " 475 -> arg: "a" 476 -> "a": "a" 477 -> ([(sep)+ arg])*: " a" 478 -> ([(sep)+ arg])*: "" 479 -> [(sep)+ arg]: " a" 480 -> (sep)+: " " 481 -> (sep)*: " " 482 -> (sep)*: "" 483 -> sep: " " 484 -> " ": " " 485 -> sep: " " 486 -> " ": " " 487 -> arg: "a" 488 -> "a": "a" 489 -> (sep)*: " " 490 -> (sep)*: " " 491 -> (sep)*: "" 492 -> sep: " " 493 -> " ": " " 494 -> sep: " " 495 -> " ": " " 496 ` 497 testLrparserMode1(t, gram, in, out) 498 } 499 500 //func TestLrparser22(t *testing.T) { 501 // gram := ` 502 // ^S = ((letter)!)+; 503 // ` 504 // in := "●a " 505 // out := ` 506 //` 507 // testLrparserMode1(t, gram, in, out) 508 //} 509 510 //---------- 511 512 func TestLrparserStop1(t *testing.T) { 513 gram := ` 514 ^id = digit (letter)*; 515 ` 516 in := "<<<●1ab>>>" 517 out := ` 518 -> ^id: "1ab" 519 -> digit: "1" 520 -> (letter)*: "ab" 521 -> (letter)*: "a" 522 -> (letter)*: "" 523 -> letter: "a" 524 -> letter: "b" 525 ` 526 testLrparserMode2(t, gram, in, out, false, true, false) 527 } 528 func TestLrparserStop2(t *testing.T) { 529 gram := ` 530 ^S = letter (linecol)?; 531 linecol = entry (entry)?; 532 entry = ":" (digit)+; 533 ` 534 in := "●a:1:++" 535 out := ` 536 -> ^S: "a:1" 537 -> letter: "a" 538 -> (linecol)?: ":1" 539 -> linecol: ":1" 540 -> entry: ":1" 541 -> ":": ":" 542 -> (digit)+: "1" 543 -> (digit)*: "" 544 -> digit: "1" 545 -> (entry)?: "" 546 ` 547 548 testLrparserMode2(t, gram, in, out, false, true, false) 549 } 550 func TestLrparserStop3(t *testing.T) { 551 gram := ` 552 ^S = (letter ":")+; 553 ` 554 in := "●a:b:c:d" 555 out := ` 556 -> ^S: "a:b:c:" 557 -> ([letter ":"])+: "a:b:c:" 558 -> ([letter ":"])*: "a:b:" 559 -> ([letter ":"])*: "a:" 560 -> ([letter ":"])*: "" 561 -> [letter ":"]: "a:" 562 -> letter: "a" 563 -> ":": ":" 564 -> [letter ":"]: "b:" 565 -> letter: "b" 566 -> ":": ":" 567 -> [letter ":"]: "c:" 568 -> letter: "c" 569 -> ":": ":" 570 ` 571 572 testLrparserMode2(t, gram, in, out, false, true, false) 573 } 574 func TestLrparserStop3b(t *testing.T) { 575 gram := ` 576 ^S = (letter ":")+ (digit)?; 577 ` 578 in := "●a:a:b" 579 out := ` 580 -> ^S: "a:a:" 581 -> ([letter ":"])+: "a:a:" 582 -> ([letter ":"])*: "a:" 583 -> ([letter ":"])*: "" 584 -> [letter ":"]: "a:" 585 -> letter: "a" 586 -> ":": ":" 587 -> [letter ":"]: "a:" 588 -> letter: "a" 589 -> ":": ":" 590 -> (digit)?: "" 591 ` 592 593 testLrparserMode2(t, gram, in, out, false, true, false) 594 } 595 func TestLrparserStop4(t *testing.T) { 596 gram := ` 597 ^S = (letter digit letter)+; 598 ` 599 in := "●a1ab2bc3" 600 out := ` 601 -> ^S: "a1ab2b" 602 -> ([letter digit letter])+: "a1ab2b" 603 -> ([letter digit letter])*: "a1a" 604 -> ([letter digit letter])*: "" 605 -> [letter digit letter]: "a1a" 606 -> letter: "a" 607 -> digit: "1" 608 -> letter: "a" 609 -> [letter digit letter]: "b2b" 610 -> letter: "b" 611 -> digit: "2" 612 -> letter: "b" 613 ` 614 615 testLrparserMode2(t, gram, in, out, false, true, false) 616 } 617 func TestLrparserStop5(t *testing.T) { 618 gram := ` 619 ^S = ("a" "b" "c" "d")+; 620 ` 621 in := "●abcdab" 622 out := ` 623 -> ^S: "abcd" 624 -> (["a" "b" "c" "d"])+: "abcd" 625 -> (["a" "b" "c" "d"])*: "" 626 -> ["a" "b" "c" "d"]: "abcd" 627 -> "a": "a" 628 -> "b": "b" 629 -> "c": "c" 630 -> "d": "d" 631 ` 632 633 testLrparserMode2(t, gram, in, out, false, true, false) 634 } 635 636 //---------- 637 638 func TestLrparserRev1(t *testing.T) { 639 gram := ` 640 ^rev = digit (letter)* ; 641 #^rev = (letter)* digit; 642 ` 643 in := "<<<1ab●>>>" 644 //in := "<<<●1ab>>>" 645 out := ` 646 -> ^rev: "1ab" 647 -> digit: "1" 648 -> (letter)*: "ab" 649 -> (letter)*: "b" 650 -> (letter)*: "" 651 -> letter: "b" 652 -> letter: "a" 653 ` 654 testLrparserMode2(t, gram, in, out, true, true, false) 655 //testLrparserMode2(t, gram, in, out, false, true, false) // no rev 656 } 657 func TestLrparserRev2(t *testing.T) { 658 gram := ` 659 ^S = (s2)?; 660 s2 = (letter)+; 661 ` 662 in := "aa●11" 663 out := ` 664 -> ^S: "aa" 665 -> (s2)?: "aa" 666 -> s2: "aa" 667 -> (letter)+: "aa" 668 -> (letter)*: "a" 669 -> (letter)*: "" 670 -> letter: "a" 671 -> letter: "a" 672 ` 673 //testLrparserMode2(t, gram, in, out, true, true) 674 testLrparserMode2(t, gram, in, out, true, false, false) 675 } 676 func TestLrparserRev3(t *testing.T) { 677 gram := ` 678 ^S = (letter|esc)+; 679 esc = @escapeAny(0,"\\"); 680 ` 681 in := "a b\\ c●" 682 out := ` 683 -> ^S: "b\\ c" 684 -> ([letter|esc])+: "b\\ c" 685 -> ([letter|esc])*: "\\ c" 686 -> ([letter|esc])*: "c" 687 -> ([letter|esc])*: "" 688 -> [letter|esc]: "c" 689 -> letter: "c" 690 -> [letter|esc]: "\\ " 691 -> esc: "\\ " 692 -> escapeAny('\\'): "\\ " 693 -> [letter|esc]: "b" 694 -> letter: "b" 695 ` 696 697 testLrparserMode2(t, gram, in, out, true, true, false) 698 } 699 func TestLrparserRev4(t *testing.T) { 700 gram := ` 701 ^S = (letter|esc)*; 702 esc = @escapeAny(0,"\\"); 703 ` 704 in := "aaa ●bbb" 705 out := ` 706 -> ^S: "" 707 -> ([letter|esc])*: "" 708 ` 709 710 bnd := testLrparserMode2(t, gram, in, out, true, true, false) 711 if bnd.Pos() != 4 { 712 t.Fatalf("bad pos: %v", bnd.Pos()) 713 } 714 } 715 func TestLrparserRev5(t *testing.T) { 716 gram := ` 717 ^S = (@escapeAny(0,esc))+ (esc)+; 718 esc = "\\"; 719 ` 720 in := "aaa\\:\\ \\● bb" 721 out := ` 722 -> ^S: "\\:\\ \\" 723 -> (escapeAny('\\'))+: "\\:\\ " 724 -> (escapeAny('\\'))*: "\\ " 725 -> (escapeAny('\\'))*: "" 726 -> escapeAny('\\'): "\\ " 727 -> escapeAny('\\'): "\\:" 728 -> (esc)+: "\\" 729 -> (esc)*: "" 730 -> esc: "\\" 731 -> "\\": "\\" 732 ` 733 bnd := testLrparserMode2(t, gram, in, out, true, true, false) 734 if bnd.End() != 3 { 735 t.Fatalf("bad pos: %v", bnd.End()) 736 } 737 } 738 739 //---------- 740 741 func TestLrparserErr1(t *testing.T) { 742 gram := ` 743 ^S = (letter)? letter; 744 ` 745 in := "●a" 746 out := `` 747 _, err := testLrparserMode3(t, gram, in, out, false, true, true) 748 t.Log(err) 749 if err == nil { 750 t.Fatal("expecting error") 751 } 752 } 753 func TestLrparserErr2(t *testing.T) { 754 gram := ` 755 ^S = (digit)+ (letter)? digit; // was endless loop "●111+a" 756 ` 757 in := "●111+a" 758 out := `` 759 _, err := testLrparserMode3(t, gram, in, out, false, true, true) 760 t.Log(err) 761 if err == nil { 762 t.Fatal("expecting error") 763 } 764 } 765 func TestLrparserErr3(t *testing.T) { 766 gram := ` 767 ^S = (digit)+ (letter)? ":"; 768 //^S = (digit)+ (letter)?; // ok 769 ` 770 in := "●111a" 771 out := `` 772 _, err := testLrparserMode3(t, gram, in, out, false, true, true) 773 t.Log(err) 774 if err == nil { 775 t.Fatal("expecting error") 776 } 777 } 778 func TestLrparserErr4(t *testing.T) { 779 gram := ` 780 ^S = ((letter)!)%; 781 ` 782 in := "●aa" 783 out := `` 784 _, err := testLrparserMode3(t, gram, in, out, false, true, true) 785 t.Log(err) 786 if err == nil { 787 t.Fatal("expecting error") 788 } 789 } 790 791 //---------- 792 793 func TestLrparserBuild1(t *testing.T) { 794 gram := ` 795 ^S = (sep)* arg ((sep)+ arg)* (sep)*; 796 arg = (letter|digit)+; 797 sep = "-"; 798 ` 799 cp := testLrparserModeB(t, gram, false, true, true) 800 801 args := []string{} 802 cp.SetBuildNodeFn("S", func(d *BuildNodeData) error { 803 //d.PrintRuleTree(5) 804 if err := d.ChildLoop(2, func(d2 *BuildNodeData) error { 805 //d2.PrintRuleTree(5) 806 args = append(args, d2.ChildStr(1)) 807 return nil 808 }); err != nil { 809 return err 810 } 811 return nil 812 }) 813 814 in := "●a1--b2-c3---d4--" 815 bnd, _, err := testLrparserModeB2(t, cp, in) 816 if err != nil { 817 t.Fatal(err) 818 } 819 _ = bnd 820 821 r1 := fmt.Sprintf("%v", args) 822 if r1 != "[b2 c3 d4]" { 823 t.Fatal(r1) 824 } 825 } 826 827 func TestLrparserBuild2(t *testing.T) { 828 gram := ` 829 ^S = args; 830 args = (sep)+ arg args | nil; 831 arg = (letter|digit)+; 832 sep = "-"; 833 ` 834 cp := testLrparserModeB(t, gram, false, true, true) 835 836 args := []string{} 837 cp.SetBuildNodeFn("S", func(d *BuildNodeData) error { 838 //d.PrintRuleTree(5) 839 if err := d.ChildLoop2(0, 2, func(d2 *BuildNodeData) error { 840 // d2 is "args" rule 841 if d2.ChildsLen() == 3 { 842 args = append(args, d2.ChildStr(1)) 843 } 844 return nil 845 }, nil); err != nil { 846 return err 847 } 848 return nil 849 }) 850 851 in := "●-a1-b2-c3--d4" 852 bnd, _, err := testLrparserModeB2(t, cp, in) 853 if err != nil { 854 t.Fatal(err) 855 } 856 _ = bnd 857 858 r1 := fmt.Sprintf("%v", args) 859 if r1 != "[a1 b2 c3 d4]" { 860 t.Fatal(r1) 861 } 862 } 863 864 //---------- 865 //---------- 866 //---------- 867 868 func testLrparserMode1(t *testing.T, gram, in, out string) *BuildNodeData { 869 t.Helper() 870 return testLrparserMode2(t, gram, in, out, false, false, false) 871 } 872 func testLrparserMode2(t *testing.T, gram, in, out string, reverse, earlyStop, shiftOnSRConflict bool) *BuildNodeData { 873 t.Helper() 874 bnd, err := testLrparserMode3(t, gram, in, out, reverse, earlyStop, shiftOnSRConflict) 875 if err != nil { 876 t.Fatal(err) 877 } 878 return bnd 879 } 880 func testLrparserMode3(t *testing.T, gram, in, out string, reverse, earlyStop, shiftOnSRConflict bool) (*BuildNodeData, error) { 881 t.Helper() 882 883 //in = string(bytes.TrimRight(in, "\n")) 884 //out = string(bytes.TrimRight(out, "\n")) 885 886 in2, index, err := testutil.SourceCursor("●", string(in), 0) 887 if err != nil { 888 return nil, err 889 } 890 891 lrp, err := NewLrparserFromString(gram) 892 if err != nil { 893 return nil, err 894 } 895 896 opt := &CpOpt{ 897 StartRule: "", 898 Reverse: reverse, 899 EarlyStop: earlyStop, 900 ShiftOnSRConflict: shiftOnSRConflict, 901 VerboseError: true, 902 } 903 cp, err := lrp.ContentParser(opt) 904 if err != nil { 905 return nil, err 906 } 907 908 bnd, cpr, err := cp.Parse([]byte(in2), index) 909 if err != nil { 910 return nil, err 911 } 912 res := bnd.SprintRuleTree(-1) 913 914 if *updateOutputFlag { 915 fmt.Printf("%v\n", sprintTaggedOut(res)) 916 } 917 918 res2 := testutil.TrimLineSpaces(res) 919 expect2 := testutil.TrimLineSpaces(out) 920 if res2 != expect2 { 921 _ = cpr 922 return nil, fmt.Errorf("%v\n%s", res, cpr.Debug(cp)) 923 //return nil, fmt.Errorf("%v", res) 924 } 925 return bnd, nil 926 } 927 928 //---------- 929 930 func testLrparserModeB(t *testing.T, gram string, reverse, earlyStop, shiftOnSRConflict bool) *ContentParser { 931 t.Helper() 932 933 lrp, err := NewLrparserFromString(gram) 934 if err != nil { 935 t.Fatal(err) 936 } 937 938 opt := &CpOpt{ 939 StartRule: "", 940 Reverse: reverse, 941 EarlyStop: earlyStop, 942 ShiftOnSRConflict: shiftOnSRConflict, 943 VerboseError: true, 944 } 945 cp, err := lrp.ContentParser(opt) 946 if err != nil { 947 t.Fatal(err) 948 } 949 return cp 950 } 951 func testLrparserModeB2(t *testing.T, cp *ContentParser, in string) (*BuildNodeData, *cpRun, error) { 952 in2, index, err := testutil.SourceCursor("●", string(in), 0) 953 if err != nil { 954 t.Fatal(err) 955 } 956 return cp.Parse([]byte(in2), index) 957 } 958 959 //---------- 960 //---------- 961 //---------- 962 963 // WARNING: used for rewriting this file (yes, writes itself) with updated tests output when changing output formats 964 func TestUpdateOutput(t *testing.T) { 965 return // MANUALLY DISABLED 966 967 if !*updateOutputFlag { 968 return 969 } 970 if err := updateOutput(t); err != nil { 971 t.Fatal(err) 972 } 973 } 974 func updateOutput(tt *testing.T) error { 975 // configuration 976 filename := "lrparser_test.go" 977 //testNamePrefix := "TestLrparser" 978 //testNamePrefix := "TestLrparserStop" 979 testNamePrefix := "TestLrparserRev" 980 contentVarName := "out" 981 982 src, err := os.ReadFile(filename) 983 if err != nil { 984 return err 985 } 986 fset := token.NewFileSet() 987 mode := parser.Mode(parser.ParseComments) 988 astFile, err := parser.ParseFile(fset, filename, src, mode) 989 if err != nil { 990 return err 991 } 992 993 vis := (func(c *astutil.Cursor) bool)(nil) 994 replace := (func(c *astutil.Cursor, val string) bool)(nil) 995 996 // find test and run it 997 vis = func(c *astutil.Cursor) bool { 998 switch t := c.Node().(type) { 999 case *ast.FuncDecl: 1000 funcName := t.Name.Name 1001 if strings.HasPrefix(funcName, testNamePrefix) { 1002 // run test 1003 funcName += "$" // ensure match 1004 fmt.Printf("running: %v\n", funcName) 1005 cmd := exec.Command("go", "test", fmt.Sprintf("-run=%v", funcName), "-update") 1006 out, err := cmd.Output() 1007 if err != nil { 1008 _ = err 1009 //tt.Fatal(err) 1010 //break 1011 } 1012 // parse wanted output 1013 out2, ok := parseTaggedOut(string(out)) 1014 if !ok { 1015 //tt.Fatalf("unable to parse tagged output:\n%q", out2) 1016 //fmt.Printf("no tagged output:\n%v", out2) 1017 fmt.Printf("\tno tagged output\n") 1018 break 1019 } 1020 out3 := "`\n" + indentStr("\t\t", out2) + "`" 1021 //fmt.Printf("out: %s\n", out3) 1022 1023 // replace var content 1024 vis3 := func(c *astutil.Cursor) bool { 1025 if ok := replace(c, out3); ok { 1026 fmt.Printf("\treplaced\n") 1027 return false 1028 } 1029 return true 1030 } 1031 _ = astutil.Apply(c.Node(), vis3, nil) 1032 } 1033 } 1034 return true 1035 } 1036 // replace var content 1037 replace = func(c *astutil.Cursor, val string) bool { 1038 switch t := c.Node().(type) { 1039 case *ast.AssignStmt: 1040 if len(t.Lhs) >= 1 { 1041 if id, ok := t.Lhs[0].(*ast.Ident); ok { 1042 if id.Name == contentVarName { 1043 //fmt.Printf("***%v\n", id) 1044 if len(t.Rhs) == 1 { 1045 bl := &ast.BasicLit{ 1046 Kind: token.STRING, 1047 Value: val, 1048 } 1049 t.Rhs[0] = bl 1050 return true 1051 } 1052 } 1053 } 1054 } 1055 } 1056 return false 1057 } 1058 1059 node := astutil.Apply(astFile, vis, nil) 1060 1061 src2, err := astut.SprintNode2(fset, node) 1062 if err != nil { 1063 return err 1064 } 1065 //fmt.Println(src2) 1066 //filename += "AA" // TESTING 1067 if err := os.WriteFile(filename, []byte(src2), 0o644); err != nil { 1068 return err 1069 } 1070 1071 return nil 1072 } 1073 1074 //---------- 1075 //---------- 1076 //---------- 1077 1078 var updateOutputFlag = flag.Bool("update", false, "") 1079 1080 var parseOutTag1 = "=[output:start]=" 1081 var parseOutTag2 = "=[output:end]=" 1082 1083 func sprintTaggedOut(s string) string { 1084 return fmt.Sprintf("%v\n%v\n%v", parseOutTag1, s, parseOutTag2) 1085 } 1086 func parseTaggedOut(s string) (string, bool) { 1087 i1 := strings.Index(s, parseOutTag1) 1088 i2 := strings.Index(s, parseOutTag2) 1089 if i1 < 0 || i2 < 0 { 1090 return s, false 1091 } 1092 return s[i1+len(parseOutTag1)+1 : i2-1], true // +1-1 for the newlines 1093 }