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