github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/parser/parser_test.go (about) 1 package parser_test 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "strings" 7 "testing" 8 9 "github.com/bingoohuang/gg/pkg/yaml/ast" 10 "github.com/bingoohuang/gg/pkg/yaml/lexer" 11 "github.com/bingoohuang/gg/pkg/yaml/parser" 12 ) 13 14 func TestParser(t *testing.T) { 15 sources := []string{ 16 "null\n", 17 "{}\n", 18 "v: hi\n", 19 "v: \"true\"\n", 20 "v: \"false\"\n", 21 "v: true\n", 22 "v: false\n", 23 "v: 10\n", 24 "v: -10\n", 25 "v: 42\n", 26 "v: 4294967296\n", 27 "v: \"10\"\n", 28 "v: 0.1\n", 29 "v: 0.99\n", 30 "v: -0.1\n", 31 "v: .inf\n", 32 "v: -.inf\n", 33 "v: .nan\n", 34 "v: null\n", 35 "v: \"\"\n", 36 "v:\n- A\n- B\n", 37 "a: '-'\n", 38 "123\n", 39 "hello: world\n", 40 "a: null\n", 41 "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", 42 "a:\n b: c\n", 43 "a: {x: 1}\n", 44 "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", 45 "a: [1, 2]\n", 46 "a: {b: c, d: e}\n", 47 "a: 3s\n", 48 "a: <foo>\n", 49 "a: \"1:1\"\n", 50 "a: 1.2.3.4\n", 51 "a: \"2015-02-24T18:19:39Z\"\n", 52 "a: 'b: c'\n", 53 "a: 'Hello #comment'\n", 54 "a: abc <<def>> ghi", 55 "a: <<abcd", 56 "a: <<:abcd", 57 "a: << :abcd", 58 "a: 100.5\n", 59 "a: bogus\n", 60 "a: \"\\0\"\n", 61 "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", 62 " a : b \n", 63 "a: b # comment\nb: c\n", 64 "---\na: b\n", 65 "a: b\n...\n", 66 "%YAML 1.2\n---\n", 67 "a: !!binary gIGC\n", 68 "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", 69 "- !tag\n a: b\n c: d\n", 70 "v:\n- A\n- |-\n B\n C\n", 71 "v:\n- A\n- >-\n B\n C\n", 72 "v: |-\n 0\n", 73 "v: |-\n 0\nx: 0", 74 `"a\n1\nb"`, 75 `{"a":"b"}`, 76 `!!map { 77 ? !!str "explicit":!!str "entry", 78 ? !!str "implicit" : !!str "entry", 79 ? !!null "" : !!null "", 80 }`, 81 } 82 for _, src := range sources { 83 if _, err := parser.Parse(lexer.Tokenize(src), 0); err != nil { 84 t.Fatalf("parse error: source [%s]: %+v", src, err) 85 } 86 } 87 } 88 89 func TestParseComplicatedDocument(t *testing.T) { 90 tests := []struct { 91 source string 92 expect string 93 }{ 94 { 95 ` 96 american: 97 - Boston Red Sox 98 - Detroit Tigers 99 - New York Yankees 100 national: 101 - New York Mets 102 - Chicago Cubs 103 - Atlanta Braves 104 `, ` 105 american: 106 - Boston Red Sox 107 - Detroit Tigers 108 - New York Yankees 109 national: 110 - New York Mets 111 - Chicago Cubs 112 - Atlanta Braves 113 `, 114 }, 115 { 116 ` 117 a: 118 b: c 119 d: e 120 f: g 121 h: 122 i: j 123 k: 124 l: m 125 n: o 126 p: q 127 r: s 128 `, ` 129 a: 130 b: c 131 d: e 132 f: g 133 h: 134 i: j 135 k: 136 l: m 137 n: o 138 p: q 139 r: s 140 `, 141 }, 142 { 143 ` 144 - a: 145 - b 146 - c 147 - d 148 `, ` 149 - a: 150 - b 151 - c 152 - d 153 `, 154 }, 155 { 156 ` 157 - a 158 - b 159 - c 160 - d 161 - e 162 - f 163 `, ` 164 - a 165 - b 166 - c - d - e 167 - f 168 `, 169 }, 170 { 171 ` 172 a: 0 - 1 173 `, 174 ` 175 a: 0 - 1 176 `, 177 }, 178 { 179 ` 180 - a: 181 b: c 182 d: e 183 - f: 184 g: h 185 `, 186 ` 187 - a: 188 b: c 189 d: e 190 - f: null 191 g: h 192 `, 193 }, 194 { 195 ` 196 a: 197 b 198 c 199 d: e 200 `, ` 201 a: b c 202 d: e 203 `, 204 }, 205 { 206 ` 207 a 208 b 209 c 210 `, ` 211 a b c 212 `, 213 }, 214 { 215 ` 216 a: 217 - b 218 - c 219 `, ` 220 a: 221 - b 222 - c 223 `, 224 }, 225 { 226 ` 227 - a : 228 b: c 229 `, ` 230 - a: null 231 b: c 232 `, 233 }, 234 { 235 ` 236 - a: 237 b 238 c 239 d 240 hoge: fuga 241 `, ` 242 - a: b c d 243 hoge: fuga 244 `, 245 }, 246 { 247 ` 248 - a # ' " # - : % 249 - b # " # - : % ' 250 - c # # - : % ' " 251 - d # - : % ' " # 252 - e # : % ' " # - 253 - f # % ' : # - : 254 `, 255 ` 256 - a 257 - b 258 - c 259 - d 260 - e 261 - f 262 `, 263 }, 264 { 265 ` 266 # comment 267 a: # comment 268 # comment 269 b: c # comment 270 # comment 271 d: e # comment 272 # comment 273 `, 274 ` 275 a: 276 b: c 277 d: e 278 `, 279 }, 280 { 281 ` 282 a: b#notcomment 283 `, 284 ` 285 a: b#notcomment 286 `, 287 }, 288 { 289 ` 290 anchored: &anchor foo 291 aliased: *anchor 292 `, 293 ` 294 anchored: &anchor foo 295 aliased: *anchor 296 `, 297 }, 298 { 299 ` 300 --- 301 - &CENTER { x: 1, y: 2 } 302 - &LEFT { x: 0, y: 2 } 303 - &BIG { r: 10 } 304 - &SMALL { r: 1 } 305 306 # All the following maps are equal: 307 308 - # Explicit keys 309 x: 1 310 y: 2 311 r: 10 312 label: center/big 313 314 - # Merge one map 315 << : *CENTER 316 r: 10 317 label: center/big 318 319 - # Merge multiple maps 320 << : [ *CENTER, *BIG ] 321 label: center/big 322 323 - # Override 324 << : [ *BIG, *LEFT, *SMALL ] 325 x: 1 326 label: center/big 327 `, 328 ` 329 --- 330 - &CENTER {x: 1, y: 2} 331 - &LEFT {x: 0, y: 2} 332 - &BIG {r: 10} 333 - &SMALL {r: 1} 334 - x: 1 335 y: 2 336 r: 10 337 label: center/big 338 - <<: *CENTER 339 r: 10 340 label: center/big 341 - <<: [*CENTER, *BIG] 342 label: center/big 343 - <<: [*BIG, *LEFT, *SMALL] 344 x: 1 345 label: center/big 346 `, 347 }, 348 { 349 ` 350 a: 351 - - b 352 - - c 353 - d 354 `, 355 ` 356 a: 357 - - b 358 - - c 359 - d 360 `, 361 }, 362 { 363 ` 364 a: 365 b: 366 c: d 367 e: 368 f: g 369 h: i 370 j: k 371 `, 372 ` 373 a: 374 b: 375 c: d 376 e: 377 f: g 378 h: i 379 j: k 380 `, 381 }, 382 { 383 ` 384 --- 385 a: 1 386 b: 2 387 ... 388 --- 389 c: 3 390 d: 4 391 ... 392 `, 393 ` 394 --- 395 a: 1 396 b: 2 397 ... 398 --- 399 c: 3 400 d: 4 401 ... 402 `, 403 }, 404 { 405 ` 406 a: 407 b: | 408 { 409 [ 1, 2 ] 410 } 411 c: d 412 `, 413 ` 414 a: 415 b: | 416 { 417 [ 1, 2 ] 418 } 419 c: d 420 `, 421 }, 422 { 423 ` 424 | 425 hoge 426 fuga 427 piyo`, 428 ` 429 | 430 hoge 431 fuga 432 piyo 433 `, 434 }, 435 { 436 ` 437 a: | 438 bbbbbbb 439 440 441 ccccccc 442 d: eeeeeeeeeeeeeeeee 443 `, 444 ` 445 a: | 446 bbbbbbb 447 448 449 ccccccc 450 d: eeeeeeeeeeeeeeeee 451 `, 452 }, 453 { 454 ` 455 a: b 456 c 457 `, 458 ` 459 a: b c 460 `, 461 }, 462 { 463 ` 464 a: 465 b: c 466 `, 467 ` 468 a: 469 b: c 470 `, 471 }, 472 { 473 ` 474 a: b 475 c: d 476 `, 477 ` 478 a: b 479 c: d 480 `, 481 }, 482 { 483 ` 484 - ab - cd 485 - ef - gh 486 `, 487 ` 488 - ab - cd 489 - ef - gh 490 `, 491 }, 492 { 493 ` 494 - 0 - 1 495 - 2 - 3 496 `, 497 ` 498 - 0 - 1 - 2 - 3 499 `, 500 }, 501 { 502 ` 503 a - b - c: value 504 `, 505 ` 506 a - b - c: value 507 `, 508 }, 509 { 510 ` 511 a: 512 - 513 b: c 514 d: e 515 - 516 f: g 517 h: i 518 `, 519 ` 520 a: 521 - b: c 522 d: e 523 - f: g 524 h: i 525 `, 526 }, 527 { 528 ` 529 a: |- 530 value 531 b: c 532 `, 533 ` 534 a: |- 535 value 536 b: c 537 `, 538 }, 539 { 540 ` 541 a: |+ 542 value 543 b: c 544 `, 545 ` 546 a: |+ 547 value 548 b: c 549 `, 550 }, 551 } 552 553 for _, test := range tests { 554 tokens := lexer.Tokenize(test.source) 555 f, err := parser.Parse(tokens, 0) 556 if err != nil { 557 t.Fatalf("%+v", err) 558 } 559 var v Visitor 560 for _, doc := range f.Docs { 561 ast.Walk(&v, doc.Body) 562 } 563 expect := fmt.Sprintf("\n%+v\n", f) 564 if test.expect != expect { 565 tokens.Dump() 566 t.Fatalf("unexpected output: [%s] != [%s]", test.expect, expect) 567 } 568 } 569 } 570 571 func TestNewLineChar(t *testing.T) { 572 for _, f := range []string{ 573 "lf.yml", 574 "cr.yml", 575 "crlf.yml", 576 } { 577 ast, err := parser.ParseFile(filepath.Join("testdata", f), 0) 578 if err != nil { 579 t.Fatalf("%+v", err) 580 } 581 actual := fmt.Sprintf("%v\n", ast) 582 expect := `a: "a" 583 b: 1 584 ` 585 if expect != actual { 586 t.Fatal("unexpected result") 587 } 588 } 589 } 590 591 func TestSyntaxError(t *testing.T) { 592 tests := []struct { 593 source string 594 expect string 595 }{ 596 { 597 ` 598 a: 599 - b 600 c: d 601 e: f 602 g: h`, 603 ` 604 [3:3] unexpected key name 605 2 | a: 606 > 3 | - b 607 4 | c: d 608 ^ 609 5 | e: f 610 6 | g: h`, 611 }, 612 { 613 ` 614 a 615 - b: c`, 616 ` 617 [2:1] unexpected key name 618 > 2 | a 619 3 | - b: c 620 ^ 621 `, 622 }, 623 { 624 `%YAML 1.1 {}`, 625 ` 626 [1:2] unexpected directive value. document not started 627 > 1 | %YAML 1.1 {} 628 ^ 629 `, 630 }, 631 { 632 `{invalid`, 633 ` 634 [1:2] unexpected map 635 > 1 | {invalid 636 ^ 637 `, 638 }, 639 { 640 `{ "key": "value" `, 641 ` 642 [1:1] unterminated flow mapping 643 > 1 | { "key": "value" 644 ^ 645 `, 646 }, 647 } 648 for _, test := range tests { 649 t.Run(test.source, func(t *testing.T) { 650 _, err := parser.ParseBytes([]byte(test.source), 0) 651 if err == nil { 652 t.Fatal("cannot catch syntax error") 653 } 654 actual := "\n" + err.Error() 655 if test.expect != actual { 656 t.Fatalf("expected: [%s] but got [%s]", test.expect, actual) 657 } 658 }) 659 } 660 } 661 662 func TestComment(t *testing.T) { 663 tests := []struct { 664 name string 665 yaml string 666 }{ 667 { 668 name: "map with comment", 669 yaml: ` 670 # commentA 671 a: #commentB 672 # commentC 673 b: c # commentD 674 # commentE 675 d: e # commentF 676 # commentG 677 f: g # commentH 678 # commentI 679 f: g # commentJ 680 # commentK 681 `, 682 }, 683 { 684 name: "sequence with comment", 685 yaml: ` 686 # commentA 687 - a # commentB 688 # commentC 689 - b: # commentD 690 # commentE 691 - d # commentF 692 - e # commentG 693 # commentH 694 `, 695 }, 696 { 697 name: "anchor and alias", 698 yaml: ` 699 a: &x b # commentA 700 c: *x # commentB 701 `, 702 }, 703 { 704 name: "multiline", 705 yaml: ` 706 # foo comment 707 # foo comment2 708 foo: # map key comment 709 # bar above comment 710 # bar above comment2 711 bar: 10 # comment for bar 712 # baz above comment 713 # baz above comment2 714 baz: bbbb # comment for baz 715 piyo: # sequence key comment 716 # sequence1 above comment 1 717 # sequence1 above comment 2 718 - sequence1 # sequence1 719 # sequence2 above comment 1 720 # sequence2 above comment 2 721 - sequence2 # sequence2 722 # sequence3 above comment 1 723 # sequence3 above comment 2 724 - false # sequence3 725 # foo2 comment 726 # foo2 comment2 727 foo2: &anchor text # anchor comment 728 # foo3 comment 729 # foo3 comment2 730 foo3: *anchor # alias comment 731 `, 732 }, 733 { 734 name: "literal", 735 yaml: ` 736 foo: | # comment 737 x: 42 738 `, 739 }, 740 { 741 name: "folded", 742 yaml: ` 743 foo: > # comment 744 x: 42 745 `, 746 }, 747 } 748 for _, test := range tests { 749 t.Run(test.name, func(t *testing.T) { 750 f, err := parser.ParseBytes([]byte(test.yaml), parser.ParseComments) 751 if err != nil { 752 t.Fatalf("%+v", err) 753 } 754 got := "\n" + f.String() + "\n" 755 if test.yaml != got { 756 t.Fatalf("expected:%s\ngot:%s", test.yaml, got) 757 } 758 }) 759 } 760 } 761 762 type Visitor struct{} 763 764 func (v *Visitor) Visit(node ast.Node) ast.Visitor { 765 tk := node.GetToken() 766 tk.Prev = nil 767 tk.Next = nil 768 return v 769 }