1 // Copyright 2014 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package parser 16 17 import ( 18 "bytes" 19 "reflect" 20 "strconv" 21 "strings" 22 "testing" 23 "text/scanner" 24 ) 25 26 func mkpos(offset, line, column int) scanner.Position { 27 return scanner.Position{ 28 Offset: offset, 29 Line: line, 30 Column: column, 31 } 32 } 33 34 var validParseTestCases = []struct { 35 input string 36 defs []Definition 37 comments []*CommentGroup 38 }{ 39 {` 40 foo {} 41 `, 42 []Definition{ 43 &Module{ 44 Type: "foo", 45 TypePos: mkpos(3, 2, 3), 46 Map: Map{ 47 LBracePos: mkpos(7, 2, 7), 48 RBracePos: mkpos(8, 2, 8), 49 }, 50 }, 51 }, 52 nil, 53 }, 54 55 {` 56 foo { 57 name: "abc", 58 } 59 `, 60 []Definition{ 61 &Module{ 62 Type: "foo", 63 TypePos: mkpos(3, 2, 3), 64 Map: Map{ 65 LBracePos: mkpos(7, 2, 7), 66 RBracePos: mkpos(27, 4, 3), 67 Properties: []*Property{ 68 { 69 Name: "name", 70 NamePos: mkpos(12, 3, 4), 71 ColonPos: mkpos(16, 3, 8), 72 Value: &String{ 73 LiteralPos: mkpos(18, 3, 10), 74 Value: "abc", 75 }, 76 }, 77 }, 78 }, 79 }, 80 }, 81 nil, 82 }, 83 84 {` 85 foo { 86 isGood: true, 87 } 88 `, 89 []Definition{ 90 &Module{ 91 Type: "foo", 92 TypePos: mkpos(3, 2, 3), 93 Map: Map{ 94 LBracePos: mkpos(7, 2, 7), 95 RBracePos: mkpos(28, 4, 3), 96 Properties: []*Property{ 97 { 98 Name: "isGood", 99 NamePos: mkpos(12, 3, 4), 100 ColonPos: mkpos(18, 3, 10), 101 Value: &Bool{ 102 LiteralPos: mkpos(20, 3, 12), 103 Value: true, 104 Token: "true", 105 }, 106 }, 107 }, 108 }, 109 }, 110 }, 111 nil, 112 }, 113 114 {` 115 foo { 116 num: 4, 117 } 118 `, 119 []Definition{ 120 &Module{ 121 Type: "foo", 122 TypePos: mkpos(3, 2, 3), 123 Map: Map{ 124 LBracePos: mkpos(7, 2, 7), 125 RBracePos: mkpos(22, 4, 3), 126 Properties: []*Property{ 127 { 128 Name: "num", 129 NamePos: mkpos(12, 3, 4), 130 ColonPos: mkpos(15, 3, 7), 131 Value: &Int64{ 132 LiteralPos: mkpos(17, 3, 9), 133 Value: 4, 134 Token: "4", 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 nil, 142 }, 143 144 {` 145 foo { 146 stuff: ["asdf", "jkl;", "qwert", 147 "uiop", "bnm,"] 148 } 149 `, 150 []Definition{ 151 &Module{ 152 Type: "foo", 153 TypePos: mkpos(3, 2, 3), 154 Map: Map{ 155 LBracePos: mkpos(7, 2, 7), 156 RBracePos: mkpos(67, 5, 3), 157 Properties: []*Property{ 158 { 159 Name: "stuff", 160 NamePos: mkpos(12, 3, 4), 161 ColonPos: mkpos(17, 3, 9), 162 Value: &List{ 163 LBracePos: mkpos(19, 3, 11), 164 RBracePos: mkpos(63, 4, 19), 165 Values: []Expression{ 166 &String{ 167 LiteralPos: mkpos(20, 3, 12), 168 Value: "asdf", 169 }, 170 &String{ 171 LiteralPos: mkpos(28, 3, 20), 172 Value: "jkl;", 173 }, 174 &String{ 175 LiteralPos: mkpos(36, 3, 28), 176 Value: "qwert", 177 }, 178 &String{ 179 LiteralPos: mkpos(49, 4, 5), 180 Value: "uiop", 181 }, 182 &String{ 183 LiteralPos: mkpos(57, 4, 13), 184 Value: "bnm,", 185 }, 186 }, 187 }, 188 }, 189 }, 190 }, 191 }, 192 }, 193 nil, 194 }, 195 196 {` 197 foo { 198 stuff: { 199 isGood: true, 200 name: "bar", 201 num: 36, 202 } 203 } 204 `, 205 []Definition{ 206 &Module{ 207 Type: "foo", 208 TypePos: mkpos(3, 2, 3), 209 Map: Map{ 210 LBracePos: mkpos(7, 2, 7), 211 RBracePos: mkpos(76, 8, 3), 212 Properties: []*Property{ 213 { 214 Name: "stuff", 215 NamePos: mkpos(12, 3, 4), 216 ColonPos: mkpos(17, 3, 9), 217 Value: &Map{ 218 LBracePos: mkpos(19, 3, 11), 219 RBracePos: mkpos(72, 7, 4), 220 Properties: []*Property{ 221 { 222 Name: "isGood", 223 NamePos: mkpos(25, 4, 5), 224 ColonPos: mkpos(31, 4, 11), 225 Value: &Bool{ 226 LiteralPos: mkpos(33, 4, 13), 227 Value: true, 228 Token: "true", 229 }, 230 }, 231 { 232 Name: "name", 233 NamePos: mkpos(43, 5, 5), 234 ColonPos: mkpos(47, 5, 9), 235 Value: &String{ 236 LiteralPos: mkpos(49, 5, 11), 237 Value: "bar", 238 }, 239 }, 240 { 241 Name: "num", 242 NamePos: mkpos(60, 6, 5), 243 ColonPos: mkpos(63, 6, 8), 244 Value: &Int64{ 245 LiteralPos: mkpos(65, 6, 10), 246 Value: 36, 247 Token: "36", 248 }, 249 }, 250 }, 251 }, 252 }, 253 }, 254 }, 255 }, 256 }, 257 nil, 258 }, 259 260 {` 261 // comment1 262 foo /* test */ { 263 // comment2 264 isGood: true, // comment3 265 } 266 `, 267 []Definition{ 268 &Module{ 269 Type: "foo", 270 TypePos: mkpos(17, 3, 3), 271 Map: Map{ 272 LBracePos: mkpos(32, 3, 18), 273 RBracePos: mkpos(81, 6, 3), 274 Properties: []*Property{ 275 { 276 Name: "isGood", 277 NamePos: mkpos(52, 5, 4), 278 ColonPos: mkpos(58, 5, 10), 279 Value: &Bool{ 280 LiteralPos: mkpos(60, 5, 12), 281 Value: true, 282 Token: "true", 283 }, 284 }, 285 }, 286 }, 287 }, 288 }, 289 []*CommentGroup{ 290 { 291 Comments: []*Comment{ 292 &Comment{ 293 Comment: []string{"// comment1"}, 294 Slash: mkpos(3, 2, 3), 295 }, 296 }, 297 }, 298 { 299 Comments: []*Comment{ 300 &Comment{ 301 Comment: []string{"/* test */"}, 302 Slash: mkpos(21, 3, 7), 303 }, 304 }, 305 }, 306 { 307 Comments: []*Comment{ 308 &Comment{ 309 Comment: []string{"// comment2"}, 310 Slash: mkpos(37, 4, 4), 311 }, 312 }, 313 }, 314 { 315 Comments: []*Comment{ 316 &Comment{ 317 Comment: []string{"// comment3"}, 318 Slash: mkpos(67, 5, 19), 319 }, 320 }, 321 }, 322 }, 323 }, 324 325 {` 326 foo { 327 name: "abc", 328 num: 4, 329 } 330 331 bar { 332 name: "def", 333 num: -5, 334 } 335 `, 336 []Definition{ 337 &Module{ 338 Type: "foo", 339 TypePos: mkpos(3, 2, 3), 340 Map: Map{ 341 LBracePos: mkpos(7, 2, 7), 342 RBracePos: mkpos(38, 5, 3), 343 Properties: []*Property{ 344 { 345 Name: "name", 346 NamePos: mkpos(12, 3, 4), 347 ColonPos: mkpos(16, 3, 8), 348 Value: &String{ 349 LiteralPos: mkpos(18, 3, 10), 350 Value: "abc", 351 }, 352 }, 353 { 354 Name: "num", 355 NamePos: mkpos(28, 4, 4), 356 ColonPos: mkpos(31, 4, 7), 357 Value: &Int64{ 358 LiteralPos: mkpos(33, 4, 9), 359 Value: 4, 360 Token: "4", 361 }, 362 }, 363 }, 364 }, 365 }, 366 &Module{ 367 Type: "bar", 368 TypePos: mkpos(43, 7, 3), 369 Map: Map{ 370 LBracePos: mkpos(47, 7, 7), 371 RBracePos: mkpos(79, 10, 3), 372 Properties: []*Property{ 373 { 374 Name: "name", 375 NamePos: mkpos(52, 8, 4), 376 ColonPos: mkpos(56, 8, 8), 377 Value: &String{ 378 LiteralPos: mkpos(58, 8, 10), 379 Value: "def", 380 }, 381 }, 382 { 383 Name: "num", 384 NamePos: mkpos(68, 9, 4), 385 ColonPos: mkpos(71, 9, 7), 386 Value: &Int64{ 387 LiteralPos: mkpos(73, 9, 9), 388 Value: -5, 389 Token: "-5", 390 }, 391 }, 392 }, 393 }, 394 }, 395 }, 396 nil, 397 }, 398 399 {` 400 foo = "stuff" 401 bar = foo 402 baz = foo + bar 403 boo = baz 404 boo += foo 405 `, 406 []Definition{ 407 &Assignment{ 408 Name: "foo", 409 NamePos: mkpos(3, 2, 3), 410 EqualsPos: mkpos(7, 2, 7), 411 Value: &String{ 412 LiteralPos: mkpos(9, 2, 9), 413 Value: "stuff", 414 }, 415 OrigValue: &String{ 416 LiteralPos: mkpos(9, 2, 9), 417 Value: "stuff", 418 }, 419 Assigner: "=", 420 Referenced: true, 421 }, 422 &Assignment{ 423 Name: "bar", 424 NamePos: mkpos(19, 3, 3), 425 EqualsPos: mkpos(23, 3, 7), 426 Value: &Variable{ 427 Name: "foo", 428 NamePos: mkpos(25, 3, 9), 429 Value: &String{ 430 LiteralPos: mkpos(9, 2, 9), 431 Value: "stuff", 432 }, 433 }, 434 OrigValue: &Variable{ 435 Name: "foo", 436 NamePos: mkpos(25, 3, 9), 437 Value: &String{ 438 LiteralPos: mkpos(9, 2, 9), 439 Value: "stuff", 440 }, 441 }, 442 Assigner: "=", 443 Referenced: true, 444 }, 445 &Assignment{ 446 Name: "baz", 447 NamePos: mkpos(31, 4, 3), 448 EqualsPos: mkpos(35, 4, 7), 449 Value: &Operator{ 450 OperatorPos: mkpos(41, 4, 13), 451 Operator: '+', 452 Value: &String{ 453 LiteralPos: mkpos(9, 2, 9), 454 Value: "stuffstuff", 455 }, 456 Args: [2]Expression{ 457 &Variable{ 458 Name: "foo", 459 NamePos: mkpos(37, 4, 9), 460 Value: &String{ 461 LiteralPos: mkpos(9, 2, 9), 462 Value: "stuff", 463 }, 464 }, 465 &Variable{ 466 Name: "bar", 467 NamePos: mkpos(43, 4, 15), 468 Value: &Variable{ 469 Name: "foo", 470 NamePos: mkpos(25, 3, 9), 471 Value: &String{ 472 LiteralPos: mkpos(9, 2, 9), 473 Value: "stuff", 474 }, 475 }, 476 }, 477 }, 478 }, 479 OrigValue: &Operator{ 480 OperatorPos: mkpos(41, 4, 13), 481 Operator: '+', 482 Value: &String{ 483 LiteralPos: mkpos(9, 2, 9), 484 Value: "stuffstuff", 485 }, 486 Args: [2]Expression{ 487 &Variable{ 488 Name: "foo", 489 NamePos: mkpos(37, 4, 9), 490 Value: &String{ 491 LiteralPos: mkpos(9, 2, 9), 492 Value: "stuff", 493 }, 494 }, 495 &Variable{ 496 Name: "bar", 497 NamePos: mkpos(43, 4, 15), 498 Value: &Variable{ 499 Name: "foo", 500 NamePos: mkpos(25, 3, 9), 501 Value: &String{ 502 LiteralPos: mkpos(9, 2, 9), 503 Value: "stuff", 504 }, 505 }, 506 }, 507 }, 508 }, 509 Assigner: "=", 510 Referenced: true, 511 }, 512 &Assignment{ 513 Name: "boo", 514 NamePos: mkpos(49, 5, 3), 515 EqualsPos: mkpos(53, 5, 7), 516 Value: &Operator{ 517 Args: [2]Expression{ 518 &Variable{ 519 Name: "baz", 520 NamePos: mkpos(55, 5, 9), 521 Value: &Operator{ 522 OperatorPos: mkpos(41, 4, 13), 523 Operator: '+', 524 Value: &String{ 525 LiteralPos: mkpos(9, 2, 9), 526 Value: "stuffstuff", 527 }, 528 Args: [2]Expression{ 529 &Variable{ 530 Name: "foo", 531 NamePos: mkpos(37, 4, 9), 532 Value: &String{ 533 LiteralPos: mkpos(9, 2, 9), 534 Value: "stuff", 535 }, 536 }, 537 &Variable{ 538 Name: "bar", 539 NamePos: mkpos(43, 4, 15), 540 Value: &Variable{ 541 Name: "foo", 542 NamePos: mkpos(25, 3, 9), 543 Value: &String{ 544 LiteralPos: mkpos(9, 2, 9), 545 Value: "stuff", 546 }, 547 }, 548 }, 549 }, 550 }, 551 }, 552 &Variable{ 553 Name: "foo", 554 NamePos: mkpos(68, 6, 10), 555 Value: &String{ 556 LiteralPos: mkpos(9, 2, 9), 557 Value: "stuff", 558 }, 559 }, 560 }, 561 OperatorPos: mkpos(66, 6, 8), 562 Operator: '+', 563 Value: &String{ 564 LiteralPos: mkpos(9, 2, 9), 565 Value: "stuffstuffstuff", 566 }, 567 }, 568 OrigValue: &Variable{ 569 Name: "baz", 570 NamePos: mkpos(55, 5, 9), 571 Value: &Operator{ 572 OperatorPos: mkpos(41, 4, 13), 573 Operator: '+', 574 Value: &String{ 575 LiteralPos: mkpos(9, 2, 9), 576 Value: "stuffstuff", 577 }, 578 Args: [2]Expression{ 579 &Variable{ 580 Name: "foo", 581 NamePos: mkpos(37, 4, 9), 582 Value: &String{ 583 LiteralPos: mkpos(9, 2, 9), 584 Value: "stuff", 585 }, 586 }, 587 &Variable{ 588 Name: "bar", 589 NamePos: mkpos(43, 4, 15), 590 Value: &Variable{ 591 Name: "foo", 592 NamePos: mkpos(25, 3, 9), 593 Value: &String{ 594 LiteralPos: mkpos(9, 2, 9), 595 Value: "stuff", 596 }, 597 }, 598 }, 599 }, 600 }, 601 }, 602 Assigner: "=", 603 }, 604 &Assignment{ 605 Name: "boo", 606 NamePos: mkpos(61, 6, 3), 607 EqualsPos: mkpos(66, 6, 8), 608 Value: &Variable{ 609 Name: "foo", 610 NamePos: mkpos(68, 6, 10), 611 Value: &String{ 612 LiteralPos: mkpos(9, 2, 9), 613 Value: "stuff", 614 }, 615 }, 616 OrigValue: &Variable{ 617 Name: "foo", 618 NamePos: mkpos(68, 6, 10), 619 Value: &String{ 620 LiteralPos: mkpos(9, 2, 9), 621 Value: "stuff", 622 }, 623 }, 624 Assigner: "+=", 625 }, 626 }, 627 nil, 628 }, 629 630 {` 631 baz = -4 + -5 + 6 632 `, 633 []Definition{ 634 &Assignment{ 635 Name: "baz", 636 NamePos: mkpos(3, 2, 3), 637 EqualsPos: mkpos(7, 2, 7), 638 Value: &Operator{ 639 OperatorPos: mkpos(12, 2, 12), 640 Operator: '+', 641 Value: &Int64{ 642 LiteralPos: mkpos(9, 2, 9), 643 Value: -3, 644 }, 645 Args: [2]Expression{ 646 &Int64{ 647 LiteralPos: mkpos(9, 2, 9), 648 Value: -4, 649 Token: "-4", 650 }, 651 &Operator{ 652 OperatorPos: mkpos(17, 2, 17), 653 Operator: '+', 654 Value: &Int64{ 655 LiteralPos: mkpos(14, 2, 14), 656 Value: 1, 657 }, 658 Args: [2]Expression{ 659 &Int64{ 660 LiteralPos: mkpos(14, 2, 14), 661 Value: -5, 662 Token: "-5", 663 }, 664 &Int64{ 665 LiteralPos: mkpos(19, 2, 19), 666 Value: 6, 667 Token: "6", 668 }, 669 }, 670 }, 671 }, 672 }, 673 OrigValue: &Operator{ 674 OperatorPos: mkpos(12, 2, 12), 675 Operator: '+', 676 Value: &Int64{ 677 LiteralPos: mkpos(9, 2, 9), 678 Value: -3, 679 }, 680 Args: [2]Expression{ 681 &Int64{ 682 LiteralPos: mkpos(9, 2, 9), 683 Value: -4, 684 Token: "-4", 685 }, 686 &Operator{ 687 OperatorPos: mkpos(17, 2, 17), 688 Operator: '+', 689 Value: &Int64{ 690 LiteralPos: mkpos(14, 2, 14), 691 Value: 1, 692 }, 693 Args: [2]Expression{ 694 &Int64{ 695 LiteralPos: mkpos(14, 2, 14), 696 Value: -5, 697 Token: "-5", 698 }, 699 &Int64{ 700 LiteralPos: mkpos(19, 2, 19), 701 Value: 6, 702 Token: "6", 703 }, 704 }, 705 }, 706 }, 707 }, 708 Assigner: "=", 709 Referenced: false, 710 }, 711 }, 712 nil, 713 }, 714 715 {` 716 foo = 1000000 717 bar = foo 718 baz = foo + bar 719 boo = baz 720 boo += foo 721 `, 722 []Definition{ 723 &Assignment{ 724 Name: "foo", 725 NamePos: mkpos(3, 2, 3), 726 EqualsPos: mkpos(7, 2, 7), 727 Value: &Int64{ 728 LiteralPos: mkpos(9, 2, 9), 729 Value: 1000000, 730 Token: "1000000", 731 }, 732 OrigValue: &Int64{ 733 LiteralPos: mkpos(9, 2, 9), 734 Value: 1000000, 735 Token: "1000000", 736 }, 737 Assigner: "=", 738 Referenced: true, 739 }, 740 &Assignment{ 741 Name: "bar", 742 NamePos: mkpos(19, 3, 3), 743 EqualsPos: mkpos(23, 3, 7), 744 Value: &Variable{ 745 Name: "foo", 746 NamePos: mkpos(25, 3, 9), 747 Value: &Int64{ 748 LiteralPos: mkpos(9, 2, 9), 749 Value: 1000000, 750 Token: "1000000", 751 }, 752 }, 753 OrigValue: &Variable{ 754 Name: "foo", 755 NamePos: mkpos(25, 3, 9), 756 Value: &Int64{ 757 LiteralPos: mkpos(9, 2, 9), 758 Value: 1000000, 759 Token: "1000000", 760 }, 761 }, 762 Assigner: "=", 763 Referenced: true, 764 }, 765 &Assignment{ 766 Name: "baz", 767 NamePos: mkpos(31, 4, 3), 768 EqualsPos: mkpos(35, 4, 7), 769 Value: &Operator{ 770 OperatorPos: mkpos(41, 4, 13), 771 Operator: '+', 772 Value: &Int64{ 773 LiteralPos: mkpos(9, 2, 9), 774 Value: 2000000, 775 }, 776 Args: [2]Expression{ 777 &Variable{ 778 Name: "foo", 779 NamePos: mkpos(37, 4, 9), 780 Value: &Int64{ 781 LiteralPos: mkpos(9, 2, 9), 782 Value: 1000000, 783 Token: "1000000", 784 }, 785 }, 786 &Variable{ 787 Name: "bar", 788 NamePos: mkpos(43, 4, 15), 789 Value: &Variable{ 790 Name: "foo", 791 NamePos: mkpos(25, 3, 9), 792 Value: &Int64{ 793 LiteralPos: mkpos(9, 2, 9), 794 Value: 1000000, 795 Token: "1000000", 796 }, 797 }, 798 }, 799 }, 800 }, 801 OrigValue: &Operator{ 802 OperatorPos: mkpos(41, 4, 13), 803 Operator: '+', 804 Value: &Int64{ 805 LiteralPos: mkpos(9, 2, 9), 806 Value: 2000000, 807 }, 808 Args: [2]Expression{ 809 &Variable{ 810 Name: "foo", 811 NamePos: mkpos(37, 4, 9), 812 Value: &Int64{ 813 LiteralPos: mkpos(9, 2, 9), 814 Value: 1000000, 815 Token: "1000000", 816 }, 817 }, 818 &Variable{ 819 Name: "bar", 820 NamePos: mkpos(43, 4, 15), 821 Value: &Variable{ 822 Name: "foo", 823 NamePos: mkpos(25, 3, 9), 824 Value: &Int64{ 825 LiteralPos: mkpos(9, 2, 9), 826 Value: 1000000, 827 Token: "1000000", 828 }, 829 }, 830 }, 831 }, 832 }, 833 Assigner: "=", 834 Referenced: true, 835 }, 836 &Assignment{ 837 Name: "boo", 838 NamePos: mkpos(49, 5, 3), 839 EqualsPos: mkpos(53, 5, 7), 840 Value: &Operator{ 841 Args: [2]Expression{ 842 &Variable{ 843 Name: "baz", 844 NamePos: mkpos(55, 5, 9), 845 Value: &Operator{ 846 OperatorPos: mkpos(41, 4, 13), 847 Operator: '+', 848 Value: &Int64{ 849 LiteralPos: mkpos(9, 2, 9), 850 Value: 2000000, 851 }, 852 Args: [2]Expression{ 853 &Variable{ 854 Name: "foo", 855 NamePos: mkpos(37, 4, 9), 856 Value: &Int64{ 857 LiteralPos: mkpos(9, 2, 9), 858 Value: 1000000, 859 Token: "1000000", 860 }, 861 }, 862 &Variable{ 863 Name: "bar", 864 NamePos: mkpos(43, 4, 15), 865 Value: &Variable{ 866 Name: "foo", 867 NamePos: mkpos(25, 3, 9), 868 Value: &Int64{ 869 LiteralPos: mkpos(9, 2, 9), 870 Value: 1000000, 871 Token: "1000000", 872 }, 873 }, 874 }, 875 }, 876 }, 877 }, 878 &Variable{ 879 Name: "foo", 880 NamePos: mkpos(68, 6, 10), 881 Value: &Int64{ 882 LiteralPos: mkpos(9, 2, 9), 883 Value: 1000000, 884 Token: "1000000", 885 }, 886 }, 887 }, 888 OperatorPos: mkpos(66, 6, 8), 889 Operator: '+', 890 Value: &Int64{ 891 LiteralPos: mkpos(9, 2, 9), 892 Value: 3000000, 893 }, 894 }, 895 OrigValue: &Variable{ 896 Name: "baz", 897 NamePos: mkpos(55, 5, 9), 898 Value: &Operator{ 899 OperatorPos: mkpos(41, 4, 13), 900 Operator: '+', 901 Value: &Int64{ 902 LiteralPos: mkpos(9, 2, 9), 903 Value: 2000000, 904 }, 905 Args: [2]Expression{ 906 &Variable{ 907 Name: "foo", 908 NamePos: mkpos(37, 4, 9), 909 Value: &Int64{ 910 LiteralPos: mkpos(9, 2, 9), 911 Value: 1000000, 912 Token: "1000000", 913 }, 914 }, 915 &Variable{ 916 Name: "bar", 917 NamePos: mkpos(43, 4, 15), 918 Value: &Variable{ 919 Name: "foo", 920 NamePos: mkpos(25, 3, 9), 921 Value: &Int64{ 922 LiteralPos: mkpos(9, 2, 9), 923 Value: 1000000, 924 Token: "1000000", 925 }, 926 }, 927 }, 928 }, 929 }, 930 }, 931 Assigner: "=", 932 }, 933 &Assignment{ 934 Name: "boo", 935 NamePos: mkpos(61, 6, 3), 936 EqualsPos: mkpos(66, 6, 8), 937 Value: &Variable{ 938 Name: "foo", 939 NamePos: mkpos(68, 6, 10), 940 Value: &Int64{ 941 LiteralPos: mkpos(9, 2, 9), 942 Value: 1000000, 943 Token: "1000000", 944 }, 945 }, 946 OrigValue: &Variable{ 947 Name: "foo", 948 NamePos: mkpos(68, 6, 10), 949 Value: &Int64{ 950 LiteralPos: mkpos(9, 2, 9), 951 Value: 1000000, 952 Token: "1000000", 953 }, 954 }, 955 Assigner: "+=", 956 }, 957 }, 958 nil, 959 }, 960 961 {` 962 // comment1 963 // comment2 964 965 /* comment3 966 comment4 */ 967 // comment5 968 969 /* comment6 */ /* comment7 */ // comment8 970 `, 971 nil, 972 []*CommentGroup{ 973 { 974 Comments: []*Comment{ 975 &Comment{ 976 Comment: []string{"// comment1"}, 977 Slash: mkpos(3, 2, 3), 978 }, 979 &Comment{ 980 Comment: []string{"// comment2"}, 981 Slash: mkpos(17, 3, 3), 982 }, 983 }, 984 }, 985 { 986 Comments: []*Comment{ 987 &Comment{ 988 Comment: []string{"/* comment3", " comment4 */"}, 989 Slash: mkpos(32, 5, 3), 990 }, 991 &Comment{ 992 Comment: []string{"// comment5"}, 993 Slash: mkpos(63, 7, 3), 994 }, 995 }, 996 }, 997 { 998 Comments: []*Comment{ 999 &Comment{ 1000 Comment: []string{"/* comment6 */"}, 1001 Slash: mkpos(78, 9, 3), 1002 }, 1003 &Comment{ 1004 Comment: []string{"/* comment7 */"}, 1005 Slash: mkpos(93, 9, 18), 1006 }, 1007 &Comment{ 1008 Comment: []string{"// comment8"}, 1009 Slash: mkpos(108, 9, 33), 1010 }, 1011 }, 1012 }, 1013 }, 1014 }, 1015 } 1016 1017 func TestParseValidInput(t *testing.T) { 1018 for i, testCase := range validParseTestCases { 1019 t.Run(strconv.Itoa(i), func(t *testing.T) { 1020 r := bytes.NewBufferString(testCase.input) 1021 file, errs := ParseAndEval("", r, NewScope(nil)) 1022 if len(errs) != 0 { 1023 t.Errorf("test case: %s", testCase.input) 1024 t.Errorf("unexpected errors:") 1025 for _, err := range errs { 1026 t.Errorf(" %s", err) 1027 } 1028 t.FailNow() 1029 } 1030 1031 if len(file.Defs) == len(testCase.defs) { 1032 for i := range file.Defs { 1033 if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) { 1034 t.Errorf("test case: %s", testCase.input) 1035 t.Errorf("incorrect defintion %d:", i) 1036 t.Errorf(" expected: %s", testCase.defs[i]) 1037 t.Errorf(" got: %s", file.Defs[i]) 1038 } 1039 } 1040 } else { 1041 t.Errorf("test case: %s", testCase.input) 1042 t.Errorf("length mismatch, expected %d definitions, got %d", 1043 len(testCase.defs), len(file.Defs)) 1044 } 1045 1046 if len(file.Comments) == len(testCase.comments) { 1047 for i := range file.Comments { 1048 if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) { 1049 t.Errorf("test case: %s", testCase.input) 1050 t.Errorf("incorrect comment %d:", i) 1051 t.Errorf(" expected: %s", testCase.comments[i]) 1052 t.Errorf(" got: %s", file.Comments[i]) 1053 } 1054 } 1055 } else { 1056 t.Errorf("test case: %s", testCase.input) 1057 t.Errorf("length mismatch, expected %d comments, got %d", 1058 len(testCase.comments), len(file.Comments)) 1059 } 1060 }) 1061 } 1062 } 1063 1064 // TODO: Test error strings 1065 1066 func TestParserEndPos(t *testing.T) { 1067 in := ` 1068 module { 1069 string: "string", 1070 stringexp: "string1" + "string2", 1071 int: -1, 1072 intexp: -1 + 2, 1073 list: ["a", "b"], 1074 listexp: ["c"] + ["d"], 1075 multilinelist: [ 1076 "e", 1077 "f", 1078 ], 1079 map: { 1080 prop: "abc", 1081 }, 1082 } 1083 ` 1084 1085 // Strip each line to make it easier to compute the previous "," from each property 1086 lines := strings.Split(in, "\n") 1087 for i := range lines { 1088 lines[i] = strings.TrimSpace(lines[i]) 1089 } 1090 in = strings.Join(lines, "\n") 1091 1092 r := bytes.NewBufferString(in) 1093 1094 file, errs := ParseAndEval("", r, NewScope(nil)) 1095 if len(errs) != 0 { 1096 t.Errorf("unexpected errors:") 1097 for _, err := range errs { 1098 t.Errorf(" %s", err) 1099 } 1100 t.FailNow() 1101 } 1102 1103 mod := file.Defs[0].(*Module) 1104 modEnd := mkpos(len(in)-1, len(lines)-1, 2) 1105 if mod.End() != modEnd { 1106 t.Errorf("expected mod.End() %s, got %s", modEnd, mod.End()) 1107 } 1108 1109 nextPos := make([]scanner.Position, len(mod.Properties)) 1110 for i := 0; i < len(mod.Properties)-1; i++ { 1111 nextPos[i] = mod.Properties[i+1].Pos() 1112 } 1113 nextPos[len(mod.Properties)-1] = mod.RBracePos 1114 1115 for i, cur := range mod.Properties { 1116 endOffset := nextPos[i].Offset - len(",\n") 1117 endLine := nextPos[i].Line - 1 1118 endColumn := len(lines[endLine-1]) // scanner.Position.Line is starts at 1 1119 endPos := mkpos(endOffset, endLine, endColumn) 1120 if cur.End() != endPos { 1121 t.Errorf("expected property %s End() %s@%d, got %s@%d", cur.Name, endPos, endPos.Offset, cur.End(), cur.End().Offset) 1122 } 1123 } 1124 } 1125