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 15package parser 16 17import ( 18 "bytes" 19 "reflect" 20 "strconv" 21 "strings" 22 "testing" 23 "text/scanner" 24) 25 26func mkpos(offset, line, column int) scanner.Position { 27 return scanner.Position{ 28 Offset: offset, 29 Line: line, 30 Column: column, 31 } 32} 33 34var 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 ` 198 foo { 199 list_of_maps: [ 200 { 201 var: true, 202 name: "a", 203 }, 204 { 205 var: false, 206 name: "b", 207 }, 208 ], 209 } 210`, 211 []Definition{ 212 &Module{ 213 Type: "foo", 214 TypePos: mkpos(3, 2, 3), 215 Map: Map{ 216 LBracePos: mkpos(7, 2, 7), 217 RBracePos: mkpos(127, 13, 3), 218 Properties: []*Property{ 219 { 220 Name: "list_of_maps", 221 NamePos: mkpos(12, 3, 4), 222 ColonPos: mkpos(24, 3, 16), 223 Value: &List{ 224 LBracePos: mkpos(26, 3, 18), 225 RBracePos: mkpos(122, 12, 4), 226 Values: []Expression{ 227 &Map{ 228 LBracePos: mkpos(32, 4, 5), 229 RBracePos: mkpos(70, 7, 5), 230 Properties: []*Property{ 231 { 232 Name: "var", 233 NamePos: mkpos(39, 5, 6), 234 ColonPos: mkpos(42, 5, 9), 235 Value: &Bool{ 236 LiteralPos: mkpos(44, 5, 11), 237 Value: true, 238 Token: "true", 239 }, 240 }, 241 { 242 Name: "name", 243 NamePos: mkpos(55, 6, 6), 244 ColonPos: mkpos(59, 6, 10), 245 Value: &String{ 246 LiteralPos: mkpos(61, 6, 12), 247 Value: "a", 248 }, 249 }, 250 }, 251 }, 252 &Map{ 253 LBracePos: mkpos(77, 8, 5), 254 RBracePos: mkpos(116, 11, 5), 255 Properties: []*Property{ 256 { 257 Name: "var", 258 NamePos: mkpos(84, 9, 6), 259 ColonPos: mkpos(87, 9, 9), 260 Value: &Bool{ 261 LiteralPos: mkpos(89, 9, 11), 262 Value: false, 263 Token: "false", 264 }, 265 }, 266 { 267 Name: "name", 268 NamePos: mkpos(101, 10, 6), 269 ColonPos: mkpos(105, 10, 10), 270 Value: &String{ 271 LiteralPos: mkpos(107, 10, 12), 272 Value: "b", 273 }, 274 }, 275 }, 276 }, 277 }, 278 }, 279 }, 280 }, 281 }, 282 }, 283 }, 284 nil, 285 }, 286 { 287 ` 288 foo { 289 list_of_lists: [ 290 [ "a", "b" ], 291 [ "c", "d" ] 292 ], 293 } 294`, 295 []Definition{ 296 &Module{ 297 Type: "foo", 298 TypePos: mkpos(3, 2, 3), 299 Map: Map{ 300 LBracePos: mkpos(7, 2, 7), 301 RBracePos: mkpos(72, 7, 3), 302 Properties: []*Property{ 303 { 304 Name: "list_of_lists", 305 NamePos: mkpos(12, 3, 4), 306 ColonPos: mkpos(25, 3, 17), 307 Value: &List{ 308 LBracePos: mkpos(27, 3, 19), 309 RBracePos: mkpos(67, 6, 4), 310 Values: []Expression{ 311 &List{ 312 LBracePos: mkpos(33, 4, 5), 313 RBracePos: mkpos(44, 4, 16), 314 Values: []Expression{ 315 &String{ 316 LiteralPos: mkpos(35, 4, 7), 317 Value: "a", 318 }, 319 &String{ 320 LiteralPos: mkpos(40, 4, 12), 321 Value: "b", 322 }, 323 }, 324 }, 325 &List{ 326 LBracePos: mkpos(51, 5, 5), 327 RBracePos: mkpos(62, 5, 16), 328 Values: []Expression{ 329 &String{ 330 LiteralPos: mkpos(53, 5, 7), 331 Value: "c", 332 }, 333 &String{ 334 LiteralPos: mkpos(58, 5, 12), 335 Value: "d", 336 }, 337 }, 338 }, 339 }, 340 }, 341 }, 342 }, 343 }, 344 }, 345 }, 346 nil, 347 }, 348 {` 349 foo { 350 stuff: { 351 isGood: true, 352 name: "bar", 353 num: 36, 354 } 355 } 356 `, 357 []Definition{ 358 &Module{ 359 Type: "foo", 360 TypePos: mkpos(3, 2, 3), 361 Map: Map{ 362 LBracePos: mkpos(7, 2, 7), 363 RBracePos: mkpos(76, 8, 3), 364 Properties: []*Property{ 365 { 366 Name: "stuff", 367 NamePos: mkpos(12, 3, 4), 368 ColonPos: mkpos(17, 3, 9), 369 Value: &Map{ 370 LBracePos: mkpos(19, 3, 11), 371 RBracePos: mkpos(72, 7, 4), 372 Properties: []*Property{ 373 { 374 Name: "isGood", 375 NamePos: mkpos(25, 4, 5), 376 ColonPos: mkpos(31, 4, 11), 377 Value: &Bool{ 378 LiteralPos: mkpos(33, 4, 13), 379 Value: true, 380 Token: "true", 381 }, 382 }, 383 { 384 Name: "name", 385 NamePos: mkpos(43, 5, 5), 386 ColonPos: mkpos(47, 5, 9), 387 Value: &String{ 388 LiteralPos: mkpos(49, 5, 11), 389 Value: "bar", 390 }, 391 }, 392 { 393 Name: "num", 394 NamePos: mkpos(60, 6, 5), 395 ColonPos: mkpos(63, 6, 8), 396 Value: &Int64{ 397 LiteralPos: mkpos(65, 6, 10), 398 Value: 36, 399 Token: "36", 400 }, 401 }, 402 }, 403 }, 404 }, 405 }, 406 }, 407 }, 408 }, 409 nil, 410 }, 411 412 {` 413 // comment1 414 foo /* test */ { 415 // comment2 416 isGood: true, // comment3 417 } 418 `, 419 []Definition{ 420 &Module{ 421 Type: "foo", 422 TypePos: mkpos(17, 3, 3), 423 Map: Map{ 424 LBracePos: mkpos(32, 3, 18), 425 RBracePos: mkpos(81, 6, 3), 426 Properties: []*Property{ 427 { 428 Name: "isGood", 429 NamePos: mkpos(52, 5, 4), 430 ColonPos: mkpos(58, 5, 10), 431 Value: &Bool{ 432 LiteralPos: mkpos(60, 5, 12), 433 Value: true, 434 Token: "true", 435 }, 436 }, 437 }, 438 }, 439 }, 440 }, 441 []*CommentGroup{ 442 { 443 Comments: []*Comment{ 444 &Comment{ 445 Comment: []string{"// comment1"}, 446 Slash: mkpos(3, 2, 3), 447 }, 448 }, 449 }, 450 { 451 Comments: []*Comment{ 452 &Comment{ 453 Comment: []string{"/* test */"}, 454 Slash: mkpos(21, 3, 7), 455 }, 456 }, 457 }, 458 { 459 Comments: []*Comment{ 460 &Comment{ 461 Comment: []string{"// comment2"}, 462 Slash: mkpos(37, 4, 4), 463 }, 464 }, 465 }, 466 { 467 Comments: []*Comment{ 468 &Comment{ 469 Comment: []string{"// comment3"}, 470 Slash: mkpos(67, 5, 19), 471 }, 472 }, 473 }, 474 }, 475 }, 476 477 {` 478 foo { 479 name: "abc", 480 num: 4, 481 } 482 483 bar { 484 name: "def", 485 num: -5, 486 } 487 `, 488 []Definition{ 489 &Module{ 490 Type: "foo", 491 TypePos: mkpos(3, 2, 3), 492 Map: Map{ 493 LBracePos: mkpos(7, 2, 7), 494 RBracePos: mkpos(38, 5, 3), 495 Properties: []*Property{ 496 { 497 Name: "name", 498 NamePos: mkpos(12, 3, 4), 499 ColonPos: mkpos(16, 3, 8), 500 Value: &String{ 501 LiteralPos: mkpos(18, 3, 10), 502 Value: "abc", 503 }, 504 }, 505 { 506 Name: "num", 507 NamePos: mkpos(28, 4, 4), 508 ColonPos: mkpos(31, 4, 7), 509 Value: &Int64{ 510 LiteralPos: mkpos(33, 4, 9), 511 Value: 4, 512 Token: "4", 513 }, 514 }, 515 }, 516 }, 517 }, 518 &Module{ 519 Type: "bar", 520 TypePos: mkpos(43, 7, 3), 521 Map: Map{ 522 LBracePos: mkpos(47, 7, 7), 523 RBracePos: mkpos(79, 10, 3), 524 Properties: []*Property{ 525 { 526 Name: "name", 527 NamePos: mkpos(52, 8, 4), 528 ColonPos: mkpos(56, 8, 8), 529 Value: &String{ 530 LiteralPos: mkpos(58, 8, 10), 531 Value: "def", 532 }, 533 }, 534 { 535 Name: "num", 536 NamePos: mkpos(68, 9, 4), 537 ColonPos: mkpos(71, 9, 7), 538 Value: &Int64{ 539 LiteralPos: mkpos(73, 9, 9), 540 Value: -5, 541 Token: "-5", 542 }, 543 }, 544 }, 545 }, 546 }, 547 }, 548 nil, 549 }, 550 551 {` 552 foo = "stuff" 553 bar = foo 554 baz = foo + bar 555 boo = baz 556 boo += foo 557 `, 558 []Definition{ 559 &Assignment{ 560 Name: "foo", 561 NamePos: mkpos(3, 2, 3), 562 EqualsPos: mkpos(7, 2, 7), 563 Value: &String{ 564 LiteralPos: mkpos(9, 2, 9), 565 Value: "stuff", 566 }, 567 OrigValue: &String{ 568 LiteralPos: mkpos(9, 2, 9), 569 Value: "stuff", 570 }, 571 Assigner: "=", 572 Referenced: true, 573 }, 574 &Assignment{ 575 Name: "bar", 576 NamePos: mkpos(19, 3, 3), 577 EqualsPos: mkpos(23, 3, 7), 578 Value: &Variable{ 579 Name: "foo", 580 NamePos: mkpos(25, 3, 9), 581 Value: &String{ 582 LiteralPos: mkpos(9, 2, 9), 583 Value: "stuff", 584 }, 585 }, 586 OrigValue: &Variable{ 587 Name: "foo", 588 NamePos: mkpos(25, 3, 9), 589 Value: &String{ 590 LiteralPos: mkpos(9, 2, 9), 591 Value: "stuff", 592 }, 593 }, 594 Assigner: "=", 595 Referenced: true, 596 }, 597 &Assignment{ 598 Name: "baz", 599 NamePos: mkpos(31, 4, 3), 600 EqualsPos: mkpos(35, 4, 7), 601 Value: &Operator{ 602 OperatorPos: mkpos(41, 4, 13), 603 Operator: '+', 604 Value: &String{ 605 LiteralPos: mkpos(9, 2, 9), 606 Value: "stuffstuff", 607 }, 608 Args: [2]Expression{ 609 &Variable{ 610 Name: "foo", 611 NamePos: mkpos(37, 4, 9), 612 Value: &String{ 613 LiteralPos: mkpos(9, 2, 9), 614 Value: "stuff", 615 }, 616 }, 617 &Variable{ 618 Name: "bar", 619 NamePos: mkpos(43, 4, 15), 620 Value: &Variable{ 621 Name: "foo", 622 NamePos: mkpos(25, 3, 9), 623 Value: &String{ 624 LiteralPos: mkpos(9, 2, 9), 625 Value: "stuff", 626 }, 627 }, 628 }, 629 }, 630 }, 631 OrigValue: &Operator{ 632 OperatorPos: mkpos(41, 4, 13), 633 Operator: '+', 634 Value: &String{ 635 LiteralPos: mkpos(9, 2, 9), 636 Value: "stuffstuff", 637 }, 638 Args: [2]Expression{ 639 &Variable{ 640 Name: "foo", 641 NamePos: mkpos(37, 4, 9), 642 Value: &String{ 643 LiteralPos: mkpos(9, 2, 9), 644 Value: "stuff", 645 }, 646 }, 647 &Variable{ 648 Name: "bar", 649 NamePos: mkpos(43, 4, 15), 650 Value: &Variable{ 651 Name: "foo", 652 NamePos: mkpos(25, 3, 9), 653 Value: &String{ 654 LiteralPos: mkpos(9, 2, 9), 655 Value: "stuff", 656 }, 657 }, 658 }, 659 }, 660 }, 661 Assigner: "=", 662 Referenced: true, 663 }, 664 &Assignment{ 665 Name: "boo", 666 NamePos: mkpos(49, 5, 3), 667 EqualsPos: mkpos(53, 5, 7), 668 Value: &Operator{ 669 Args: [2]Expression{ 670 &Variable{ 671 Name: "baz", 672 NamePos: mkpos(55, 5, 9), 673 Value: &Operator{ 674 OperatorPos: mkpos(41, 4, 13), 675 Operator: '+', 676 Value: &String{ 677 LiteralPos: mkpos(9, 2, 9), 678 Value: "stuffstuff", 679 }, 680 Args: [2]Expression{ 681 &Variable{ 682 Name: "foo", 683 NamePos: mkpos(37, 4, 9), 684 Value: &String{ 685 LiteralPos: mkpos(9, 2, 9), 686 Value: "stuff", 687 }, 688 }, 689 &Variable{ 690 Name: "bar", 691 NamePos: mkpos(43, 4, 15), 692 Value: &Variable{ 693 Name: "foo", 694 NamePos: mkpos(25, 3, 9), 695 Value: &String{ 696 LiteralPos: mkpos(9, 2, 9), 697 Value: "stuff", 698 }, 699 }, 700 }, 701 }, 702 }, 703 }, 704 &Variable{ 705 Name: "foo", 706 NamePos: mkpos(68, 6, 10), 707 Value: &String{ 708 LiteralPos: mkpos(9, 2, 9), 709 Value: "stuff", 710 }, 711 }, 712 }, 713 OperatorPos: mkpos(66, 6, 8), 714 Operator: '+', 715 Value: &String{ 716 LiteralPos: mkpos(9, 2, 9), 717 Value: "stuffstuffstuff", 718 }, 719 }, 720 OrigValue: &Variable{ 721 Name: "baz", 722 NamePos: mkpos(55, 5, 9), 723 Value: &Operator{ 724 OperatorPos: mkpos(41, 4, 13), 725 Operator: '+', 726 Value: &String{ 727 LiteralPos: mkpos(9, 2, 9), 728 Value: "stuffstuff", 729 }, 730 Args: [2]Expression{ 731 &Variable{ 732 Name: "foo", 733 NamePos: mkpos(37, 4, 9), 734 Value: &String{ 735 LiteralPos: mkpos(9, 2, 9), 736 Value: "stuff", 737 }, 738 }, 739 &Variable{ 740 Name: "bar", 741 NamePos: mkpos(43, 4, 15), 742 Value: &Variable{ 743 Name: "foo", 744 NamePos: mkpos(25, 3, 9), 745 Value: &String{ 746 LiteralPos: mkpos(9, 2, 9), 747 Value: "stuff", 748 }, 749 }, 750 }, 751 }, 752 }, 753 }, 754 Assigner: "=", 755 }, 756 &Assignment{ 757 Name: "boo", 758 NamePos: mkpos(61, 6, 3), 759 EqualsPos: mkpos(66, 6, 8), 760 Value: &Variable{ 761 Name: "foo", 762 NamePos: mkpos(68, 6, 10), 763 Value: &String{ 764 LiteralPos: mkpos(9, 2, 9), 765 Value: "stuff", 766 }, 767 }, 768 OrigValue: &Variable{ 769 Name: "foo", 770 NamePos: mkpos(68, 6, 10), 771 Value: &String{ 772 LiteralPos: mkpos(9, 2, 9), 773 Value: "stuff", 774 }, 775 }, 776 Assigner: "+=", 777 }, 778 }, 779 nil, 780 }, 781 782 {` 783 baz = -4 + -5 + 6 784 `, 785 []Definition{ 786 &Assignment{ 787 Name: "baz", 788 NamePos: mkpos(3, 2, 3), 789 EqualsPos: mkpos(7, 2, 7), 790 Value: &Operator{ 791 OperatorPos: mkpos(12, 2, 12), 792 Operator: '+', 793 Value: &Int64{ 794 LiteralPos: mkpos(9, 2, 9), 795 Value: -3, 796 }, 797 Args: [2]Expression{ 798 &Int64{ 799 LiteralPos: mkpos(9, 2, 9), 800 Value: -4, 801 Token: "-4", 802 }, 803 &Operator{ 804 OperatorPos: mkpos(17, 2, 17), 805 Operator: '+', 806 Value: &Int64{ 807 LiteralPos: mkpos(14, 2, 14), 808 Value: 1, 809 }, 810 Args: [2]Expression{ 811 &Int64{ 812 LiteralPos: mkpos(14, 2, 14), 813 Value: -5, 814 Token: "-5", 815 }, 816 &Int64{ 817 LiteralPos: mkpos(19, 2, 19), 818 Value: 6, 819 Token: "6", 820 }, 821 }, 822 }, 823 }, 824 }, 825 OrigValue: &Operator{ 826 OperatorPos: mkpos(12, 2, 12), 827 Operator: '+', 828 Value: &Int64{ 829 LiteralPos: mkpos(9, 2, 9), 830 Value: -3, 831 }, 832 Args: [2]Expression{ 833 &Int64{ 834 LiteralPos: mkpos(9, 2, 9), 835 Value: -4, 836 Token: "-4", 837 }, 838 &Operator{ 839 OperatorPos: mkpos(17, 2, 17), 840 Operator: '+', 841 Value: &Int64{ 842 LiteralPos: mkpos(14, 2, 14), 843 Value: 1, 844 }, 845 Args: [2]Expression{ 846 &Int64{ 847 LiteralPos: mkpos(14, 2, 14), 848 Value: -5, 849 Token: "-5", 850 }, 851 &Int64{ 852 LiteralPos: mkpos(19, 2, 19), 853 Value: 6, 854 Token: "6", 855 }, 856 }, 857 }, 858 }, 859 }, 860 Assigner: "=", 861 Referenced: false, 862 }, 863 }, 864 nil, 865 }, 866 867 {` 868 foo = 1000000 869 bar = foo 870 baz = foo + bar 871 boo = baz 872 boo += foo 873 `, 874 []Definition{ 875 &Assignment{ 876 Name: "foo", 877 NamePos: mkpos(3, 2, 3), 878 EqualsPos: mkpos(7, 2, 7), 879 Value: &Int64{ 880 LiteralPos: mkpos(9, 2, 9), 881 Value: 1000000, 882 Token: "1000000", 883 }, 884 OrigValue: &Int64{ 885 LiteralPos: mkpos(9, 2, 9), 886 Value: 1000000, 887 Token: "1000000", 888 }, 889 Assigner: "=", 890 Referenced: true, 891 }, 892 &Assignment{ 893 Name: "bar", 894 NamePos: mkpos(19, 3, 3), 895 EqualsPos: mkpos(23, 3, 7), 896 Value: &Variable{ 897 Name: "foo", 898 NamePos: mkpos(25, 3, 9), 899 Value: &Int64{ 900 LiteralPos: mkpos(9, 2, 9), 901 Value: 1000000, 902 Token: "1000000", 903 }, 904 }, 905 OrigValue: &Variable{ 906 Name: "foo", 907 NamePos: mkpos(25, 3, 9), 908 Value: &Int64{ 909 LiteralPos: mkpos(9, 2, 9), 910 Value: 1000000, 911 Token: "1000000", 912 }, 913 }, 914 Assigner: "=", 915 Referenced: true, 916 }, 917 &Assignment{ 918 Name: "baz", 919 NamePos: mkpos(31, 4, 3), 920 EqualsPos: mkpos(35, 4, 7), 921 Value: &Operator{ 922 OperatorPos: mkpos(41, 4, 13), 923 Operator: '+', 924 Value: &Int64{ 925 LiteralPos: mkpos(9, 2, 9), 926 Value: 2000000, 927 }, 928 Args: [2]Expression{ 929 &Variable{ 930 Name: "foo", 931 NamePos: mkpos(37, 4, 9), 932 Value: &Int64{ 933 LiteralPos: mkpos(9, 2, 9), 934 Value: 1000000, 935 Token: "1000000", 936 }, 937 }, 938 &Variable{ 939 Name: "bar", 940 NamePos: mkpos(43, 4, 15), 941 Value: &Variable{ 942 Name: "foo", 943 NamePos: mkpos(25, 3, 9), 944 Value: &Int64{ 945 LiteralPos: mkpos(9, 2, 9), 946 Value: 1000000, 947 Token: "1000000", 948 }, 949 }, 950 }, 951 }, 952 }, 953 OrigValue: &Operator{ 954 OperatorPos: mkpos(41, 4, 13), 955 Operator: '+', 956 Value: &Int64{ 957 LiteralPos: mkpos(9, 2, 9), 958 Value: 2000000, 959 }, 960 Args: [2]Expression{ 961 &Variable{ 962 Name: "foo", 963 NamePos: mkpos(37, 4, 9), 964 Value: &Int64{ 965 LiteralPos: mkpos(9, 2, 9), 966 Value: 1000000, 967 Token: "1000000", 968 }, 969 }, 970 &Variable{ 971 Name: "bar", 972 NamePos: mkpos(43, 4, 15), 973 Value: &Variable{ 974 Name: "foo", 975 NamePos: mkpos(25, 3, 9), 976 Value: &Int64{ 977 LiteralPos: mkpos(9, 2, 9), 978 Value: 1000000, 979 Token: "1000000", 980 }, 981 }, 982 }, 983 }, 984 }, 985 Assigner: "=", 986 Referenced: true, 987 }, 988 &Assignment{ 989 Name: "boo", 990 NamePos: mkpos(49, 5, 3), 991 EqualsPos: mkpos(53, 5, 7), 992 Value: &Operator{ 993 Args: [2]Expression{ 994 &Variable{ 995 Name: "baz", 996 NamePos: mkpos(55, 5, 9), 997 Value: &Operator{ 998 OperatorPos: mkpos(41, 4, 13), 999 Operator: '+', 1000 Value: &Int64{ 1001 LiteralPos: mkpos(9, 2, 9), 1002 Value: 2000000, 1003 }, 1004 Args: [2]Expression{ 1005 &Variable{ 1006 Name: "foo", 1007 NamePos: mkpos(37, 4, 9), 1008 Value: &Int64{ 1009 LiteralPos: mkpos(9, 2, 9), 1010 Value: 1000000, 1011 Token: "1000000", 1012 }, 1013 }, 1014 &Variable{ 1015 Name: "bar", 1016 NamePos: mkpos(43, 4, 15), 1017 Value: &Variable{ 1018 Name: "foo", 1019 NamePos: mkpos(25, 3, 9), 1020 Value: &Int64{ 1021 LiteralPos: mkpos(9, 2, 9), 1022 Value: 1000000, 1023 Token: "1000000", 1024 }, 1025 }, 1026 }, 1027 }, 1028 }, 1029 }, 1030 &Variable{ 1031 Name: "foo", 1032 NamePos: mkpos(68, 6, 10), 1033 Value: &Int64{ 1034 LiteralPos: mkpos(9, 2, 9), 1035 Value: 1000000, 1036 Token: "1000000", 1037 }, 1038 }, 1039 }, 1040 OperatorPos: mkpos(66, 6, 8), 1041 Operator: '+', 1042 Value: &Int64{ 1043 LiteralPos: mkpos(9, 2, 9), 1044 Value: 3000000, 1045 }, 1046 }, 1047 OrigValue: &Variable{ 1048 Name: "baz", 1049 NamePos: mkpos(55, 5, 9), 1050 Value: &Operator{ 1051 OperatorPos: mkpos(41, 4, 13), 1052 Operator: '+', 1053 Value: &Int64{ 1054 LiteralPos: mkpos(9, 2, 9), 1055 Value: 2000000, 1056 }, 1057 Args: [2]Expression{ 1058 &Variable{ 1059 Name: "foo", 1060 NamePos: mkpos(37, 4, 9), 1061 Value: &Int64{ 1062 LiteralPos: mkpos(9, 2, 9), 1063 Value: 1000000, 1064 Token: "1000000", 1065 }, 1066 }, 1067 &Variable{ 1068 Name: "bar", 1069 NamePos: mkpos(43, 4, 15), 1070 Value: &Variable{ 1071 Name: "foo", 1072 NamePos: mkpos(25, 3, 9), 1073 Value: &Int64{ 1074 LiteralPos: mkpos(9, 2, 9), 1075 Value: 1000000, 1076 Token: "1000000", 1077 }, 1078 }, 1079 }, 1080 }, 1081 }, 1082 }, 1083 Assigner: "=", 1084 }, 1085 &Assignment{ 1086 Name: "boo", 1087 NamePos: mkpos(61, 6, 3), 1088 EqualsPos: mkpos(66, 6, 8), 1089 Value: &Variable{ 1090 Name: "foo", 1091 NamePos: mkpos(68, 6, 10), 1092 Value: &Int64{ 1093 LiteralPos: mkpos(9, 2, 9), 1094 Value: 1000000, 1095 Token: "1000000", 1096 }, 1097 }, 1098 OrigValue: &Variable{ 1099 Name: "foo", 1100 NamePos: mkpos(68, 6, 10), 1101 Value: &Int64{ 1102 LiteralPos: mkpos(9, 2, 9), 1103 Value: 1000000, 1104 Token: "1000000", 1105 }, 1106 }, 1107 Assigner: "+=", 1108 }, 1109 }, 1110 nil, 1111 }, 1112 1113 {` 1114 // comment1 1115 // comment2 1116 1117 /* comment3 1118 comment4 */ 1119 // comment5 1120 1121 /* comment6 */ /* comment7 */ // comment8 1122 `, 1123 nil, 1124 []*CommentGroup{ 1125 { 1126 Comments: []*Comment{ 1127 &Comment{ 1128 Comment: []string{"// comment1"}, 1129 Slash: mkpos(3, 2, 3), 1130 }, 1131 &Comment{ 1132 Comment: []string{"// comment2"}, 1133 Slash: mkpos(17, 3, 3), 1134 }, 1135 }, 1136 }, 1137 { 1138 Comments: []*Comment{ 1139 &Comment{ 1140 Comment: []string{"/* comment3", " comment4 */"}, 1141 Slash: mkpos(32, 5, 3), 1142 }, 1143 &Comment{ 1144 Comment: []string{"// comment5"}, 1145 Slash: mkpos(63, 7, 3), 1146 }, 1147 }, 1148 }, 1149 { 1150 Comments: []*Comment{ 1151 &Comment{ 1152 Comment: []string{"/* comment6 */"}, 1153 Slash: mkpos(78, 9, 3), 1154 }, 1155 &Comment{ 1156 Comment: []string{"/* comment7 */"}, 1157 Slash: mkpos(93, 9, 18), 1158 }, 1159 &Comment{ 1160 Comment: []string{"// comment8"}, 1161 Slash: mkpos(108, 9, 33), 1162 }, 1163 }, 1164 }, 1165 }, 1166 }, 1167} 1168 1169func TestParseValidInput(t *testing.T) { 1170 for i, testCase := range validParseTestCases { 1171 t.Run(strconv.Itoa(i), func(t *testing.T) { 1172 r := bytes.NewBufferString(testCase.input) 1173 file, errs := ParseAndEval("", r, NewScope(nil)) 1174 if len(errs) != 0 { 1175 t.Errorf("test case: %s", testCase.input) 1176 t.Errorf("unexpected errors:") 1177 for _, err := range errs { 1178 t.Errorf(" %s", err) 1179 } 1180 t.FailNow() 1181 } 1182 1183 if len(file.Defs) == len(testCase.defs) { 1184 for i := range file.Defs { 1185 if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) { 1186 t.Errorf("test case: %s", testCase.input) 1187 t.Errorf("incorrect definition %d:", i) 1188 t.Errorf(" expected: %s", testCase.defs[i]) 1189 t.Errorf(" got: %s", file.Defs[i]) 1190 } 1191 } 1192 } else { 1193 t.Errorf("test case: %s", testCase.input) 1194 t.Errorf("length mismatch, expected %d definitions, got %d", 1195 len(testCase.defs), len(file.Defs)) 1196 } 1197 1198 if len(file.Comments) == len(testCase.comments) { 1199 for i := range file.Comments { 1200 if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) { 1201 t.Errorf("test case: %s", testCase.input) 1202 t.Errorf("incorrect comment %d:", i) 1203 t.Errorf(" expected: %s", testCase.comments[i]) 1204 t.Errorf(" got: %s", file.Comments[i]) 1205 } 1206 } 1207 } else { 1208 t.Errorf("test case: %s", testCase.input) 1209 t.Errorf("length mismatch, expected %d comments, got %d", 1210 len(testCase.comments), len(file.Comments)) 1211 } 1212 }) 1213 } 1214} 1215 1216// TODO: Test error strings 1217 1218func TestParserEndPos(t *testing.T) { 1219 in := ` 1220 module { 1221 string: "string", 1222 stringexp: "string1" + "string2", 1223 int: -1, 1224 intexp: -1 + 2, 1225 list: ["a", "b"], 1226 listexp: ["c"] + ["d"], 1227 multilinelist: [ 1228 "e", 1229 "f", 1230 ], 1231 map: { 1232 prop: "abc", 1233 }, 1234 } 1235 ` 1236 1237 // Strip each line to make it easier to compute the previous "," from each property 1238 lines := strings.Split(in, "\n") 1239 for i := range lines { 1240 lines[i] = strings.TrimSpace(lines[i]) 1241 } 1242 in = strings.Join(lines, "\n") 1243 1244 r := bytes.NewBufferString(in) 1245 1246 file, errs := ParseAndEval("", r, NewScope(nil)) 1247 if len(errs) != 0 { 1248 t.Errorf("unexpected errors:") 1249 for _, err := range errs { 1250 t.Errorf(" %s", err) 1251 } 1252 t.FailNow() 1253 } 1254 1255 mod := file.Defs[0].(*Module) 1256 modEnd := mkpos(len(in)-1, len(lines)-1, 2) 1257 if mod.End() != modEnd { 1258 t.Errorf("expected mod.End() %s, got %s", modEnd, mod.End()) 1259 } 1260 1261 nextPos := make([]scanner.Position, len(mod.Properties)) 1262 for i := 0; i < len(mod.Properties)-1; i++ { 1263 nextPos[i] = mod.Properties[i+1].Pos() 1264 } 1265 nextPos[len(mod.Properties)-1] = mod.RBracePos 1266 1267 for i, cur := range mod.Properties { 1268 endOffset := nextPos[i].Offset - len(",\n") 1269 endLine := nextPos[i].Line - 1 1270 endColumn := len(lines[endLine-1]) // scanner.Position.Line is starts at 1 1271 endPos := mkpos(endOffset, endLine, endColumn) 1272 if cur.End() != endPos { 1273 t.Errorf("expected property %s End() %s@%d, got %s@%d", cur.Name, endPos, endPos.Offset, cur.End(), cur.End().Offset) 1274 } 1275 } 1276} 1277 1278func TestParserNotEvaluated(t *testing.T) { 1279 // When parsing without evaluation, create variables correctly 1280 scope := NewScope(nil) 1281 input := "FOO=abc\n" 1282 _, errs := Parse("", bytes.NewBufferString(input), scope) 1283 if errs != nil { 1284 t.Errorf("unexpected errors:") 1285 for _, err := range errs { 1286 t.Errorf(" %s", err) 1287 } 1288 t.FailNow() 1289 } 1290 assignment, found := scope.Get("FOO") 1291 if !found { 1292 t.Fatalf("Expected to find FOO after parsing %s", input) 1293 } 1294 if s := assignment.String(); strings.Contains(s, "PANIC") { 1295 t.Errorf("Attempt to print FOO returned %s", s) 1296 } 1297} 1298