github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/parsing/parser_test.go (about) 1 package parsing 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/arnodel/golua/ast" 8 "github.com/arnodel/golua/ops" 9 "github.com/arnodel/golua/scanner" 10 "github.com/arnodel/golua/token" 11 ) 12 13 type testScanner struct { 14 *scanner.Scanner 15 } 16 17 func (s testScanner) Scan() *token.Token { 18 tok := s.Scanner.Scan() 19 if tok != nil { 20 tok.Pos = token.Pos{Offset: -1} 21 } 22 return tok 23 } 24 25 func newTestScanner(src string) Scanner { 26 return testScanner{scanner.New("test", []byte(src))} 27 } 28 29 func tok(tp token.Type, lit string) *token.Token { 30 return &token.Token{ 31 Type: tp, 32 Lit: []byte(lit), 33 Pos: token.Pos{Offset: -1}, 34 } 35 } 36 37 func name(s string) ast.Name { 38 return ast.Name{Val: s} 39 } 40 41 func nameAttrib(s string, attribs ...string) ast.NameAttrib { 42 var attrib = ast.NoAttrib 43 if len(attribs) > 0 { 44 switch attribs[0] { 45 case "close": 46 attrib = ast.CloseAttrib 47 case "const": 48 attrib = ast.ConstAttrib 49 } 50 } 51 return ast.NameAttrib{Name: name(s), Attrib: attrib} 52 } 53 54 func str(s string) ast.String { 55 return ast.String{Val: []byte(s)} 56 } 57 func TestParser_Return(t *testing.T) { 58 tests := []struct { 59 name string 60 input string 61 want []ast.ExpNode 62 want1 *token.Token 63 }{ 64 { 65 name: "Bare return without semicolon", 66 input: "return", 67 want: []ast.ExpNode{}, 68 want1: tok(token.EOF, ""), 69 }, 70 { 71 name: "Bare return with semicolon", 72 input: "return;", 73 want: []ast.ExpNode{}, 74 want1: tok(token.EOF, ""), 75 }, 76 { 77 name: "Single return without semicolon", 78 input: "return 1", 79 want: []ast.ExpNode{ast.NewInt(1)}, 80 want1: tok(token.EOF, ""), 81 }, 82 { 83 name: "Single return with semicolon", 84 input: "return 42; end", 85 want: []ast.ExpNode{ast.NewInt(42)}, 86 want1: tok(token.KwEnd, "end"), 87 }, 88 { 89 name: "Double return with semicolon", 90 input: "return 42, true end", 91 want: []ast.ExpNode{ast.NewInt(42), ast.Bool{Val: true}}, 92 want1: tok(token.KwEnd, "end"), 93 }, 94 } 95 for _, tt := range tests { 96 t.Run(tt.name, func(t *testing.T) { 97 p := &Parser{scanner: newTestScanner(tt.input)} 98 got, got1 := p.Return(p.Scan()) 99 if !reflect.DeepEqual(got, tt.want) { 100 t.Errorf("Parser.Return() got = %v, want %v", got, tt.want) 101 } 102 if !reflect.DeepEqual(got1, tt.want1) { 103 t.Errorf("Parser.Return() got1 = %v, want %v", got1, tt.want1) 104 } 105 }) 106 } 107 } 108 109 func TestParser_PrefixExp(t *testing.T) { 110 tests := []struct { 111 name string 112 input string 113 want ast.ExpNode 114 want1 *token.Token 115 }{ 116 { 117 name: "name", 118 input: "abc", 119 want: ast.Name{Val: "abc"}, 120 want1: tok(token.EOF, ""), 121 }, 122 { 123 name: "name followed by binop", 124 input: "abc +", 125 want: ast.Name{Val: "abc"}, 126 want1: tok(token.SgPlus, "+"), 127 }, 128 { 129 name: "Exp in brackets", 130 input: "(false)", 131 want: ast.Bool{}, 132 want1: tok(token.EOF, ""), 133 }, 134 { 135 name: "index", 136 input: "x[1] then", 137 want: ast.IndexExp{Coll: ast.Name{Val: "x"}, Idx: ast.NewInt(1)}, 138 want1: tok(token.KwThen, "then"), 139 }, 140 { 141 name: "dot", 142 input: "foo.bar ..", 143 want: ast.IndexExp{Coll: name("foo"), Idx: str("bar")}, 144 want1: tok(token.SgConcat, ".."), 145 }, 146 { 147 name: "method call", 148 input: "foo:bar(1)", 149 want: ast.NewFunctionCall(name("foo"), name("bar"), []ast.ExpNode{ast.NewInt(1)}), 150 want1: tok(token.EOF, ""), 151 }, 152 { 153 name: "call", 154 input: `f(x, "y") /`, 155 want: ast.NewFunctionCall(name("f"), ast.Name{}, []ast.ExpNode{name("x"), str("y")}), 156 want1: tok(token.SgSlash, "/"), 157 }, 158 { 159 name: "call in brackets", 160 input: `(f(x)) /`, 161 want: ast.NewFunctionCall(name("f"), ast.Name{}, []ast.ExpNode{name("x")}).InBrackets(), 162 want1: tok(token.SgSlash, "/"), 163 }, 164 { 165 name: "chain index, dot, function call, method call", 166 input: "x[1].abc():meth(1)", 167 want: ast.NewFunctionCall( 168 ast.NewFunctionCall( 169 ast.IndexExp{ 170 Coll: ast.IndexExp{Coll: name("x"), Idx: ast.NewInt(1)}, 171 Idx: str("abc"), 172 }, 173 ast.Name{}, 174 []ast.ExpNode{}, 175 ), 176 name("meth"), 177 []ast.ExpNode{ast.NewInt(1)}, 178 ), 179 want1: tok(token.EOF, ""), 180 }, 181 } 182 for _, tt := range tests { 183 t.Run(tt.name, func(t *testing.T) { 184 p := &Parser{scanner: newTestScanner(tt.input)} 185 got, got1 := p.PrefixExp(p.Scan()) 186 if !reflect.DeepEqual(got, tt.want) { 187 t.Errorf("Parser.PrefixExp() got = %v, want %v", got, tt.want) 188 } 189 if !reflect.DeepEqual(got1, tt.want1) { 190 t.Errorf("Parser.PrefixExp() got1 = %v, want %v", got1, tt.want1) 191 } 192 }) 193 } 194 } 195 196 func TestParser_TableConstructor(t *testing.T) { 197 tests := []struct { 198 name string 199 input string 200 want ast.TableConstructor 201 want1 *token.Token 202 }{ 203 { 204 name: "Empty table", 205 input: "{}", 206 want: ast.TableConstructor{}, 207 want1: tok(token.EOF, ""), 208 }, 209 { 210 name: "Single element table", 211 input: "{1}", 212 want: ast.TableConstructor{ 213 Fields: []ast.TableField{ 214 {Key: ast.NoTableKey{}, Value: ast.NewInt(1)}, 215 }, 216 }, 217 want1: tok(token.EOF, ""), 218 }, 219 { 220 name: "Single element table with terminating semicolon", 221 input: "{1;}", 222 want: ast.TableConstructor{ 223 Fields: []ast.TableField{ 224 {Key: ast.NoTableKey{}, Value: ast.NewInt(1)}, 225 }, 226 }, 227 want1: tok(token.EOF, ""), 228 }, 229 { 230 name: "Single element table with terminating comma", 231 input: "{1,}", 232 want: ast.TableConstructor{ 233 Fields: []ast.TableField{ 234 {Key: ast.NoTableKey{}, Value: ast.NewInt(1)}, 235 }, 236 }, 237 want1: tok(token.EOF, ""), 238 }, 239 { 240 name: "Comma separated elements table", 241 input: "{x=1,y=2}", 242 want: ast.TableConstructor{ 243 Fields: []ast.TableField{ 244 {Key: str("x"), Value: ast.NewInt(1)}, 245 {Key: str("y"), Value: ast.NewInt(2)}, 246 }, 247 }, 248 want1: tok(token.EOF, ""), 249 }, 250 { 251 name: "Semicolon separated elements table", 252 input: `{x=1;y=2;}`, 253 want: ast.TableConstructor{ 254 Fields: []ast.TableField{ 255 {Key: str("x"), Value: ast.NewInt(1)}, 256 {Key: str("y"), Value: ast.NewInt(2)}, 257 }, 258 }, 259 want1: tok(token.EOF, ""), 260 }, 261 } 262 for _, tt := range tests { 263 t.Run(tt.name, func(t *testing.T) { 264 p := &Parser{scanner: newTestScanner(tt.input)} 265 got, got1 := p.TableConstructor(p.Scan()) 266 if !reflect.DeepEqual(got, tt.want) { 267 t.Errorf("Parser.TableConstructor() got = %v, want %v", got, tt.want) 268 } 269 if !reflect.DeepEqual(got1, tt.want1) { 270 t.Errorf("Parser.TableConstructor() got1 = %v, want %v", got1, tt.want1) 271 } 272 }) 273 } 274 } 275 276 func TestParser_ShortExp(t *testing.T) { 277 tests := []struct { 278 name string 279 input string 280 want ast.ExpNode 281 want1 *token.Token 282 }{ 283 { 284 name: "nil", 285 input: "nil and", 286 want: ast.Nil{}, 287 want1: tok(token.KwAnd, "and"), 288 }, 289 { 290 name: "true", 291 input: "true or", 292 want: ast.Bool{Val: true}, 293 want1: tok(token.KwOr, "or"), 294 }, 295 { 296 name: "false", 297 input: "false or", 298 want: ast.Bool{}, 299 want1: tok(token.KwOr, "or"), 300 }, 301 { 302 name: "decimal int", 303 input: "1234", 304 want: ast.NewInt(1234), 305 want1: tok(token.EOF, ""), 306 }, 307 { 308 name: "hex int", 309 input: "0x1234", 310 want: ast.NewInt(0x1234), 311 want1: tok(token.EOF, ""), 312 }, 313 { 314 name: "decimal float", 315 input: "0.125", 316 want: ast.NewFloat(0.125), 317 want1: tok(token.EOF, ""), 318 }, 319 { 320 name: "single quoted string", 321 input: `'abc'`, 322 want: str("abc"), 323 want1: tok(token.EOF, ""), 324 }, 325 { 326 name: "double quoted string", 327 input: `"hello there"`, 328 want: str("hello there"), 329 want1: tok(token.EOF, ""), 330 }, 331 { 332 name: "long string", 333 input: `[=[[[hello]]]=]`, 334 want: str("[[hello]]"), 335 want1: tok(token.EOF, ""), 336 }, 337 { 338 name: "...", 339 input: `...`, 340 want: ast.Etc{}, 341 want1: tok(token.EOF, ""), 342 }, 343 { 344 name: "function", 345 input: `function(x) end`, 346 want: ast.Function{ 347 ParList: ast.ParList{Params: []ast.Name{name("x")}}, 348 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 349 }, 350 want1: tok(token.EOF, ""), 351 }, 352 { 353 name: "unop shortexp", 354 input: `not true`, 355 want: &ast.UnOp{Op: ops.OpNot, Operand: ast.Bool{Val: true}}, 356 want1: tok(token.EOF, ""), 357 }, 358 { 359 name: "unop unop shortexp", 360 input: `-#x`, 361 want: &ast.UnOp{ 362 Op: ops.OpNeg, 363 Operand: &ast.UnOp{Op: ops.OpLen, Operand: name("x")}, 364 }, 365 want1: tok(token.EOF, ""), 366 }, 367 { 368 name: "prefix exp", 369 input: "(x)+2", 370 want: name("x"), 371 want1: tok(token.SgPlus, "+"), 372 }, 373 { 374 name: "power", 375 input: "x^y+z", 376 want: ast.NewBinOp(name("x"), ops.OpPow, nil, name("y")), 377 want1: tok(token.SgPlus, "+"), 378 }, 379 { 380 name: "-power (power tighter than unary op)", 381 input: "-x^y+z", 382 want: &ast.UnOp{ 383 Op: ops.OpNeg, 384 Operand: ast.NewBinOp(name("x"), ops.OpPow, nil, name("y")), 385 }, 386 want1: tok(token.SgPlus, "+"), 387 }, 388 { 389 name: "x^y^z (right associative)", 390 input: "x^y^z/", 391 want: ast.NewBinOp( 392 name("x"), 393 ops.OpPow, 394 nil, 395 ast.NewBinOp(name("y"), ops.OpPow, nil, name("z")), 396 ), 397 want1: tok(token.SgSlash, "/"), 398 }, 399 } 400 for _, tt := range tests { 401 t.Run(tt.name, func(t *testing.T) { 402 p := &Parser{scanner: newTestScanner(tt.input)} 403 got, got1 := p.ShortExp(p.Scan()) 404 if !reflect.DeepEqual(got, tt.want) { 405 t.Errorf("Parser.ShortExp() got = %v, want %v", got, tt.want) 406 } 407 if !reflect.DeepEqual(got1, tt.want1) { 408 t.Errorf("Parser.ShortExp() got1 = %v, want %v", got1, tt.want1) 409 } 410 }) 411 } 412 } 413 414 func TestParser_FunctionDef(t *testing.T) { 415 tests := []struct { 416 name string 417 input string 418 want ast.Function 419 want1 *token.Token 420 }{ 421 { 422 name: "no arguments", 423 input: "() end", 424 want: ast.Function{ 425 ParList: ast.ParList{}, 426 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 427 }, 428 want1: tok(token.EOF, ""), 429 }, 430 { 431 name: "etc only", 432 input: "(...) end", 433 want: ast.Function{ 434 ParList: ast.ParList{HasDots: true}, 435 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 436 }, 437 want1: tok(token.EOF, ""), 438 }, 439 { 440 name: "one arg", 441 input: "(x) end", 442 want: ast.Function{ 443 ParList: ast.ParList{ 444 Params: []ast.Name{name("x")}, 445 }, 446 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 447 }, 448 want1: tok(token.EOF, ""), 449 }, 450 { 451 name: "two args", 452 input: "(x, y) end", 453 want: ast.Function{ 454 ParList: ast.ParList{ 455 Params: []ast.Name{name("x"), name("y")}, 456 }, 457 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 458 }, 459 want1: tok(token.EOF, ""), 460 }, 461 { 462 name: "two args and etc", 463 input: "(x, y, ...) end", 464 want: ast.Function{ 465 ParList: ast.ParList{ 466 Params: []ast.Name{name("x"), name("y")}, 467 HasDots: true, 468 }, 469 Body: ast.NewBlockStat(nil, []ast.ExpNode{}), 470 }, 471 want1: tok(token.EOF, ""), 472 }, 473 } 474 for _, tt := range tests { 475 t.Run(tt.name, func(t *testing.T) { 476 p := &Parser{scanner: newTestScanner(tt.input)} 477 got, got1 := p.FunctionDef(p.Scan()) 478 if !reflect.DeepEqual(got, tt.want) { 479 t.Errorf("Parser.FunctionDef() got = %v, want %v", got, tt.want) 480 } 481 if !reflect.DeepEqual(got1, tt.want1) { 482 t.Errorf("Parser.FunctionDef() got1 = %v, want %v", got1, tt.want1) 483 } 484 }) 485 } 486 } 487 488 func TestParser_Args(t *testing.T) { 489 tests := []struct { 490 name string 491 input string 492 want []ast.ExpNode 493 want1 *token.Token 494 }{ 495 { 496 name: "Empty brackets", 497 input: "()", 498 want: []ast.ExpNode{}, 499 want1: tok(token.EOF, ""), 500 }, 501 { 502 name: "1 arg in brackets", 503 input: "(x)", 504 want: []ast.ExpNode{name("x")}, 505 want1: tok(token.EOF, ""), 506 }, 507 { 508 name: "2 args in brackets", 509 input: "(x, y)", 510 want: []ast.ExpNode{name("x"), name("y")}, 511 want1: tok(token.EOF, ""), 512 }, 513 { 514 name: "table arg", 515 input: "{x=1}", 516 want: []ast.ExpNode{ 517 ast.TableConstructor{ 518 Fields: []ast.TableField{{Key: str("x"), Value: ast.NewInt(1)}}, 519 }, 520 }, 521 want1: tok(token.EOF, ""), 522 }, 523 { 524 name: "string arg", 525 input: `"hello"`, 526 want: []ast.ExpNode{str("hello")}, 527 want1: tok(token.EOF, ""), 528 }, 529 { 530 name: "long string arg", 531 input: `[[coucou]]`, 532 want: []ast.ExpNode{str("coucou")}, 533 want1: tok(token.EOF, ""), 534 }, 535 { 536 name: "etc arg", 537 input: "(...)", 538 want: []ast.ExpNode{ast.Etc{}}, 539 want1: tok(token.EOF, ""), 540 }, 541 } 542 for _, tt := range tests { 543 t.Run(tt.name, func(t *testing.T) { 544 p := &Parser{scanner: newTestScanner(tt.input)} 545 got, got1 := p.Args(p.Scan()) 546 if !reflect.DeepEqual(got, tt.want) { 547 t.Errorf("Parser.Args() got = %v, want %v", got, tt.want) 548 } 549 if !reflect.DeepEqual(got1, tt.want1) { 550 t.Errorf("Parser.Args() got1 = %v, want %v", got1, tt.want1) 551 } 552 }) 553 } 554 } 555 556 func TestParser_Field(t *testing.T) { 557 tests := []struct { 558 name string 559 input string 560 want ast.TableField 561 want1 *token.Token 562 }{ 563 { 564 name: "[x]=y", 565 input: "[false]=1,", 566 want: ast.TableField{Key: ast.Bool{}, Value: ast.NewInt(1)}, 567 want1: tok(token.SgComma, ","), 568 }, 569 { 570 name: "x=y", 571 input: "bar=42;", 572 want: ast.TableField{Key: str("bar"), Value: ast.NewInt(42)}, 573 want1: tok(token.SgSemicolon, ";"), 574 }, 575 { 576 name: "exp", 577 input: `'bonjour'`, 578 want: ast.TableField{Key: ast.NoTableKey{}, Value: str("bonjour")}, 579 want1: tok(token.EOF, ""), 580 }, 581 { 582 name: "exp starting with name", 583 input: "a.b", 584 want: ast.TableField{ 585 Key: ast.NoTableKey{}, 586 Value: ast.IndexExp{Coll: name("a"), Idx: str("b")}, 587 }, 588 want1: tok(token.EOF, ""), 589 }, 590 } 591 for _, tt := range tests { 592 t.Run(tt.name, func(t *testing.T) { 593 p := &Parser{scanner: newTestScanner(tt.input)} 594 got, got1 := p.Field(p.Scan()) 595 if !reflect.DeepEqual(got, tt.want) { 596 t.Errorf("Parser.Field() got = %v, want %v", got, tt.want) 597 } 598 if !reflect.DeepEqual(got1, tt.want1) { 599 t.Errorf("Parser.Field() got1 = %v, want %v", got1, tt.want1) 600 } 601 }) 602 } 603 } 604 605 func TestParser_Exp(t *testing.T) { 606 tests := []struct { 607 name string 608 input string 609 want ast.ExpNode 610 want1 *token.Token 611 }{ 612 { 613 name: "short expression", 614 input: "-x.y)", 615 want: &ast.UnOp{ 616 Op: ops.OpNeg, 617 Operand: ast.IndexExp{ 618 Coll: name("x"), 619 Idx: str("y"), 620 }, 621 }, 622 want1: tok(token.SgCloseBkt, ")"), 623 }, 624 { 625 name: "binop", 626 input: "x + y)", 627 want: ast.NewBinOp(name("x"), ops.OpAdd, nil, name("y")), 628 want1: tok(token.SgCloseBkt, ")"), 629 }, 630 { 631 name: "2 binops of precedence", 632 input: "x + y - z then", 633 want: ast.NewBinOp( 634 ast.NewBinOp(name("x"), ops.OpAdd, nil, name("y")), 635 ops.OpSub, 636 nil, 637 name("z"), 638 ), 639 want1: tok(token.KwThen, "then"), 640 }, 641 { 642 name: "2 binops of decreasing precedence", 643 input: "x * y + z then", 644 want: ast.NewBinOp( 645 ast.NewBinOp(name("x"), ops.OpMul, nil, name("y")), 646 ops.OpAdd, 647 nil, 648 name("z"), 649 ), 650 want1: tok(token.KwThen, "then"), 651 }, 652 { 653 name: "2 binops of increasing precedence", 654 input: "x | y + z then", 655 want: ast.NewBinOp( 656 name("x"), 657 ops.OpBitOr, 658 nil, 659 ast.NewBinOp(name("y"), ops.OpAdd, nil, name("z")), 660 ), 661 want1: tok(token.KwThen, "then"), 662 }, 663 { 664 name: "3 binops of decreasing precedence", 665 input: "x * y + z or t then", 666 want: ast.NewBinOp( 667 ast.NewBinOp( 668 ast.NewBinOp(name("x"), ops.OpMul, nil, name("y")), 669 ops.OpAdd, 670 nil, 671 name("z"), 672 ), 673 ops.OpOr, 674 nil, 675 name("t"), 676 ), 677 want1: tok(token.KwThen, "then"), 678 }, 679 { 680 name: "3 binops of increasing precedence", 681 input: "x << y .. z % t]", 682 want: ast.NewBinOp( 683 name("x"), 684 ops.OpShiftL, 685 nil, 686 ast.NewBinOp( 687 name("y"), 688 ops.OpConcat, 689 nil, 690 ast.NewBinOp(name("z"), ops.OpMod, nil, name("t")), 691 ), 692 ), 693 want1: tok(token.SgCloseSquareBkt, "]"), 694 }, 695 { 696 name: "concat right associative", 697 input: "x .. y .. z .. t until", 698 want: ast.NewBinOp( 699 name("x"), 700 ops.OpConcat, 701 nil, 702 ast.NewBinOp( 703 name("y"), 704 ops.OpConcat, 705 nil, 706 ast.NewBinOp(name("z"), ops.OpConcat, nil, name("t")), 707 ), 708 ), 709 want1: tok(token.KwUntil, "until"), 710 }, 711 } 712 for _, tt := range tests { 713 t.Run(tt.name, func(t *testing.T) { 714 p := &Parser{scanner: newTestScanner(tt.input)} 715 got, got1 := p.Exp(p.Scan()) 716 if !reflect.DeepEqual(got, tt.want) { 717 t.Errorf("Parser.Exp() got = %+v, want %+v", got, tt.want) 718 } 719 if !reflect.DeepEqual(got1, tt.want1) { 720 t.Errorf("Parser.Exp() got1 = %v, want %v", got1, tt.want1) 721 } 722 }) 723 } 724 } 725 726 func TestParser_Block(t *testing.T) { 727 tests := []struct { 728 name string 729 input string 730 want ast.BlockStat 731 want1 *token.Token 732 }{ 733 { 734 name: "Empty block ending in 'end'", 735 input: "end", 736 want: ast.NewBlockStat(nil, nil), 737 want1: tok(token.KwEnd, "end"), 738 }, 739 { 740 name: "Empty block ending in 'else'", 741 input: "else", 742 want: ast.NewBlockStat(nil, nil), 743 want1: tok(token.KwElse, "else"), 744 }, 745 { 746 name: "Empty block ending in 'elsif'", 747 input: "elseif", 748 want: ast.NewBlockStat(nil, nil), 749 want1: tok(token.KwElseIf, "elseif"), 750 }, 751 { 752 name: "Empty block ending in 'until'", 753 input: "until", 754 want: ast.NewBlockStat(nil, nil), 755 want1: tok(token.KwUntil, "until"), 756 }, 757 { 758 name: "Empty block ending in EOF", 759 input: "", 760 want: ast.NewBlockStat(nil, nil), 761 want1: tok(token.EOF, ""), 762 }, 763 { 764 name: "Block with return", 765 input: "break return 1", 766 want: ast.NewBlockStat( 767 []ast.Stat{ast.BreakStat{}}, 768 []ast.ExpNode{ast.NewInt(1)}, 769 ), 770 want1: tok(token.EOF, ""), 771 }, 772 { 773 name: "Block with empty return", 774 input: "break return end", 775 want: ast.NewBlockStat( 776 []ast.Stat{ast.BreakStat{}}, 777 []ast.ExpNode{}, 778 ), 779 want1: tok(token.KwEnd, "end"), 780 }, 781 { 782 name: "Block without return", 783 input: "break end", 784 want: ast.NewBlockStat( 785 []ast.Stat{ast.BreakStat{}}, 786 nil, 787 ), 788 want1: tok(token.KwEnd, "end"), 789 }, 790 } 791 for _, tt := range tests { 792 t.Run(tt.name, func(t *testing.T) { 793 p := &Parser{scanner: newTestScanner(tt.input)} 794 got, got1 := p.Block(p.Scan()) 795 if !reflect.DeepEqual(got, tt.want) { 796 t.Errorf("Parser.Block() got = %v, want %v", got, tt.want) 797 } 798 if !reflect.DeepEqual(got1, tt.want1) { 799 t.Errorf("Parser.Block() got1 = %v, want %v", got1, tt.want1) 800 } 801 }) 802 } 803 } 804 805 func TestParser_If(t *testing.T) { 806 tests := []struct { 807 name string 808 input string 809 want ast.IfStat 810 want1 *token.Token 811 }{ 812 { 813 name: "plain if ... then ... end", 814 input: "if true then ; end", 815 want: ast.IfStat{ 816 If: ast.CondStat{ 817 Cond: ast.Bool{Val: true}, 818 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 819 }, 820 }, 821 want1: tok(token.EOF, ""), 822 }, 823 { 824 name: "if ... then ... else ... end", 825 input: "if cond then ; else ; end", 826 want: ast.IfStat{ 827 If: ast.CondStat{ 828 Cond: name("cond"), 829 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 830 }, 831 Else: &ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 832 }, 833 want1: tok(token.EOF, ""), 834 }, 835 { 836 name: "if ... then ... elseif ... then ... end", 837 input: "if a then ; elseif b then ; end", 838 want: ast.IfStat{ 839 If: ast.CondStat{ 840 Cond: name("a"), 841 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 842 }, 843 ElseIfs: []ast.CondStat{{ 844 Cond: name("b"), 845 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 846 }}, 847 }, 848 want1: tok(token.EOF, ""), 849 }, 850 { 851 name: "if ... then ... elseif ... then ... else ... end", 852 input: "if a then ; elseif b then ; else ; end", 853 want: ast.IfStat{ 854 If: ast.CondStat{ 855 Cond: name("a"), 856 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 857 }, 858 ElseIfs: []ast.CondStat{{ 859 Cond: name("b"), 860 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 861 }}, 862 Else: &ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 863 }, 864 want1: tok(token.EOF, ""), 865 }, 866 { 867 name: "if ... then ... elseif ... then ... elseif ... then ... end", 868 input: "if a then ; elseif b then ; elseif c then ; end", 869 want: ast.IfStat{ 870 If: ast.CondStat{ 871 Cond: name("a"), 872 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 873 }, 874 ElseIfs: []ast.CondStat{ 875 { 876 Cond: name("b"), 877 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 878 }, 879 { 880 Cond: name("c"), 881 Body: ast.NewBlockStat([]ast.Stat{ast.EmptyStat{}}, nil), 882 }, 883 }, 884 }, 885 want1: tok(token.EOF, ""), 886 }, 887 } 888 for _, tt := range tests { 889 t.Run(tt.name, func(t *testing.T) { 890 p := &Parser{scanner: newTestScanner(tt.input)} 891 got, got1 := p.If(p.Scan()) 892 if !reflect.DeepEqual(got, tt.want) { 893 t.Errorf("Parser.If() got = %+v, want %+v", got, tt.want) 894 } 895 if !reflect.DeepEqual(got1, tt.want1) { 896 t.Errorf("Parser.If() got1 = %v, want %v", got1, tt.want1) 897 } 898 }) 899 } 900 } 901 902 func TestParser_For(t *testing.T) { 903 tests := []struct { 904 name string 905 input string 906 want ast.Stat 907 want1 *token.Token 908 }{ 909 { 910 name: "for x = 1, 2 do ; end", 911 input: "for x = 1, 2 do ; end", 912 want: &ast.ForStat{ 913 Var: name("x"), 914 Start: ast.NewInt(1), 915 Stop: ast.NewInt(2), 916 Step: ast.NewInt(1), 917 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 918 }, 919 want1: tok(token.EOF, ""), 920 }, 921 { 922 name: "for x = 1, 2, 3 do ; end", 923 input: "for x = 1, 2, 3 do ; end", 924 want: &ast.ForStat{ 925 Var: name("x"), 926 Start: ast.NewInt(1), 927 Stop: ast.NewInt(2), 928 Step: ast.NewInt(3), 929 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 930 }, 931 want1: tok(token.EOF, ""), 932 }, 933 { 934 name: "for in with one variable", 935 input: "for i in X do ; end", 936 want: &ast.ForInStat{ 937 Vars: []ast.Name{name("i")}, 938 Params: []ast.ExpNode{name("X")}, 939 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 940 }, 941 want1: tok(token.EOF, ""), 942 }, 943 { 944 name: "for in with 3 variables", 945 input: "for i, j, k in X do ; end", 946 want: &ast.ForInStat{ 947 Vars: []ast.Name{name("i"), name("j"), name("k")}, 948 Params: []ast.ExpNode{name("X")}, 949 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 950 }, 951 want1: tok(token.EOF, ""), 952 }, 953 { 954 name: "for in with 3 parameters", 955 input: "for i in X,Y,Z do ; end", 956 want: &ast.ForInStat{ 957 Vars: []ast.Name{name("i")}, 958 Params: []ast.ExpNode{name("X"), name("Y"), name("Z")}, 959 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 960 }, 961 want1: tok(token.EOF, ""), 962 }, 963 } 964 for _, tt := range tests { 965 t.Run(tt.name, func(t *testing.T) { 966 p := &Parser{scanner: newTestScanner(tt.input)} 967 got, got1 := p.For(p.Scan()) 968 if !reflect.DeepEqual(got, tt.want) { 969 t.Errorf("Parser.For() got = %v, want %v", got, tt.want) 970 } 971 if !reflect.DeepEqual(got1, tt.want1) { 972 t.Errorf("Parser.For() got1 = %v, want %v", got1, tt.want1) 973 } 974 }) 975 } 976 } 977 978 func TestParser_Local(t *testing.T) { 979 tests := []struct { 980 name string 981 input string 982 want ast.Stat 983 want1 *token.Token 984 err interface{} 985 }{ 986 { 987 name: "local function definition", 988 input: "local function f(x) return x end", 989 want: ast.LocalFunctionStat{ 990 Name: name("f"), 991 Function: ast.Function{ 992 Name: "f", 993 ParList: ast.ParList{ 994 Params: []ast.Name{name("x")}, 995 }, 996 Body: ast.BlockStat{ 997 Return: []ast.ExpNode{name("x")}, 998 }, 999 }, 1000 }, 1001 want1: tok(token.EOF, ""), 1002 }, 1003 { 1004 name: "local single variable declaration with no value", 1005 input: "local x z", 1006 want: ast.LocalStat{NameAttribs: []ast.NameAttrib{nameAttrib("x")}}, 1007 want1: tok(token.IDENT, "z"), 1008 }, 1009 { 1010 name: "local 3 variables declaration with no value", 1011 input: "local x, y, z", 1012 want: ast.LocalStat{NameAttribs: []ast.NameAttrib{nameAttrib("x"), nameAttrib("y"), nameAttrib("z")}}, 1013 want1: tok(token.EOF, ""), 1014 }, 1015 { 1016 name: "local 3 variables declaration with 1 value", 1017 input: "local x, y, z = 123", 1018 want: ast.LocalStat{ 1019 NameAttribs: []ast.NameAttrib{nameAttrib("x"), nameAttrib("y"), nameAttrib("z")}, 1020 Values: []ast.ExpNode{ast.NewInt(123)}, 1021 }, 1022 want1: tok(token.EOF, ""), 1023 }, 1024 { 1025 name: "local 2 variables declaration with 3 value", 1026 input: `local x, y = 123, "a", 'b'`, 1027 want: ast.LocalStat{ 1028 NameAttribs: []ast.NameAttrib{nameAttrib("x"), nameAttrib("y")}, 1029 Values: []ast.ExpNode{ast.NewInt(123), str("a"), str("b")}, 1030 }, 1031 want1: tok(token.EOF, ""), 1032 }, 1033 { 1034 name: "local with const attrib", 1035 input: `local x <const> = 1`, 1036 want: ast.LocalStat{ 1037 NameAttribs: []ast.NameAttrib{nameAttrib("x", "const")}, 1038 Values: []ast.ExpNode{ast.NewInt(1)}, 1039 }, 1040 want1: tok(token.EOF, ""), 1041 }, 1042 { 1043 name: "local with close attrib", 1044 input: `local x <close> = b`, 1045 want: ast.LocalStat{ 1046 NameAttribs: []ast.NameAttrib{nameAttrib("x", "close")}, 1047 Values: []ast.ExpNode{name("b")}, 1048 }, 1049 want1: tok(token.EOF, ""), 1050 }, 1051 { 1052 name: "local with invalid attrib", 1053 input: `local x <foobar> = b`, 1054 err: Error{Got: tok(token.IDENT, "foobar"), Expected: "'const' or 'close'"}, 1055 }, 1056 } 1057 for _, tt := range tests { 1058 t.Run(tt.name, func(t *testing.T) { 1059 defer func() { 1060 r := recover() 1061 if !reflect.DeepEqual(r, tt.err) { 1062 t.Errorf("Parser.Local() error = %v, want %v", r, tt.err) 1063 } 1064 }() 1065 p := &Parser{scanner: newTestScanner(tt.input)} 1066 got, got1 := p.Local(p.Scan()) 1067 if !reflect.DeepEqual(got, tt.want) { 1068 t.Errorf("Parser.Local() got = %v, want %v", got, tt.want) 1069 } 1070 if !reflect.DeepEqual(got1, tt.want1) { 1071 t.Errorf("Parser.Local() got1 = %v, want %v", got1, tt.want1) 1072 } 1073 }) 1074 } 1075 } 1076 1077 func TestParser_FunctionStat(t *testing.T) { 1078 tests := []struct { 1079 name string 1080 input string 1081 want ast.Stat 1082 want1 *token.Token 1083 }{ 1084 { 1085 name: "function with plain name", 1086 input: "function foo() end", 1087 want: ast.AssignStat{ 1088 Dest: []ast.Var{name("foo")}, 1089 Src: []ast.ExpNode{ast.Function{ 1090 Name: "foo", 1091 Body: ast.BlockStat{Return: []ast.ExpNode{}}, 1092 }}, 1093 }, 1094 want1: tok(token.EOF, ""), 1095 }, 1096 { 1097 name: "function with dotted name", 1098 input: "function foo.bar.baz() end", 1099 want: ast.AssignStat{ 1100 Dest: []ast.Var{ 1101 ast.IndexExp{ 1102 Coll: ast.IndexExp{ 1103 Coll: name("foo"), 1104 Idx: str("bar"), 1105 }, 1106 Idx: str("baz"), 1107 }}, 1108 Src: []ast.ExpNode{ast.Function{ 1109 Name: "baz", 1110 Body: ast.BlockStat{Return: []ast.ExpNode{}}, 1111 }}, 1112 }, 1113 want1: tok(token.EOF, ""), 1114 }, 1115 { 1116 name: "method", 1117 input: "function dog:bark(at) end", 1118 want: ast.AssignStat{ 1119 Dest: []ast.Var{ 1120 ast.IndexExp{ 1121 Coll: name("dog"), 1122 Idx: str("bark"), 1123 }}, 1124 Src: []ast.ExpNode{ast.Function{ 1125 Name: "bark", 1126 ParList: ast.ParList{Params: []ast.Name{name("self"), name("at")}}, 1127 Body: ast.BlockStat{Return: []ast.ExpNode{}}, 1128 }}, 1129 }, 1130 want1: tok(token.EOF, ""), 1131 }, 1132 } 1133 for _, tt := range tests { 1134 t.Run(tt.name, func(t *testing.T) { 1135 p := &Parser{scanner: newTestScanner(tt.input)} 1136 got, got1 := p.FunctionStat(p.Scan()) 1137 if !reflect.DeepEqual(got, tt.want) { 1138 t.Errorf("Parser.FunctionStat() got = %v, want %v", got, tt.want) 1139 } 1140 if !reflect.DeepEqual(got1, tt.want1) { 1141 t.Errorf("Parser.FunctionStat() got1 = %v, want %v", got1, tt.want1) 1142 } 1143 }) 1144 } 1145 } 1146 1147 func TestParser_Stat(t *testing.T) { 1148 tests := []struct { 1149 name string 1150 input string 1151 want ast.Stat 1152 want1 *token.Token 1153 }{ 1154 { 1155 name: "empty statement", 1156 input: ";", 1157 want: ast.EmptyStat{}, 1158 want1: tok(token.EOF, ""), 1159 }, 1160 { 1161 name: "break statement", 1162 input: "break", 1163 want: ast.BreakStat{}, 1164 want1: tok(token.EOF, ""), 1165 }, 1166 { 1167 name: "goto statement", 1168 input: "goto start", 1169 want: ast.GotoStat{Label: name("start")}, 1170 want1: tok(token.EOF, ""), 1171 }, 1172 { 1173 name: "do ... end block", 1174 input: "do ; end bye", 1175 want: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 1176 want1: tok(token.IDENT, "bye"), 1177 }, 1178 { 1179 name: "while block", 1180 input: "while true do ; end", 1181 want: ast.WhileStat{CondStat: ast.CondStat{ 1182 Cond: ast.Bool{Val: true}, 1183 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 1184 }}, 1185 want1: tok(token.EOF, ""), 1186 }, 1187 { 1188 name: "repeat block", 1189 input: "repeat ; until z end", 1190 want: ast.RepeatStat{CondStat: ast.CondStat{ 1191 Cond: name("z"), 1192 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 1193 }}, 1194 want1: tok(token.KwEnd, "end"), 1195 }, 1196 { 1197 name: "if statement", 1198 input: "if x then ; end", 1199 want: ast.IfStat{ 1200 If: ast.CondStat{ 1201 Cond: name("x"), 1202 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 1203 }, 1204 }, 1205 want1: tok(token.EOF, ""), 1206 }, 1207 { 1208 name: "label statement", 1209 input: "::here::", 1210 want: ast.LabelStat{Name: name("here")}, 1211 want1: tok(token.EOF, ""), 1212 }, 1213 { 1214 name: "function call", 1215 input: "foo(1, 2) bar()", 1216 want: ast.NewFunctionCall( 1217 name("foo"), 1218 ast.Name{}, 1219 []ast.ExpNode{ast.NewInt(1), ast.NewInt(2)}, 1220 ), 1221 want1: tok(token.IDENT, "bar"), 1222 }, 1223 { 1224 name: "assignement statement with one variable", 1225 input: "a = 12 for", 1226 want: ast.AssignStat{ 1227 Dest: []ast.Var{name("a")}, 1228 Src: []ast.ExpNode{ast.NewInt(12)}, 1229 }, 1230 want1: tok(token.KwFor, "for"), 1231 }, 1232 { 1233 name: "assignement statement with 3 variables", 1234 input: "a, b, c.d = 12 if", 1235 want: ast.AssignStat{ 1236 Dest: []ast.Var{ 1237 name("a"), 1238 name("b"), 1239 ast.IndexExp{Coll: name("c"), Idx: str("d")}, 1240 }, 1241 Src: []ast.ExpNode{ast.NewInt(12)}, 1242 }, 1243 want1: tok(token.KwIf, "if"), 1244 }, 1245 { 1246 name: "function with plain name", 1247 input: "function foo() end", 1248 want: ast.AssignStat{ 1249 Dest: []ast.Var{name("foo")}, 1250 Src: []ast.ExpNode{ast.Function{ 1251 Name: "foo", 1252 Body: ast.BlockStat{Return: []ast.ExpNode{}}, 1253 }}, 1254 }, 1255 want1: tok(token.EOF, ""), 1256 }, 1257 { 1258 name: "local single variable declaration with no value", 1259 input: "local x z", 1260 want: ast.LocalStat{NameAttribs: []ast.NameAttrib{nameAttrib("x")}}, 1261 want1: tok(token.IDENT, "z"), 1262 }, 1263 { 1264 name: "for x = 1, 2 do ; end", 1265 input: "for x = 1, 2 do ; end", 1266 want: &ast.ForStat{ 1267 Var: name("x"), 1268 Start: ast.NewInt(1), 1269 Stop: ast.NewInt(2), 1270 Step: ast.NewInt(1), 1271 Body: ast.BlockStat{Stats: []ast.Stat{ast.EmptyStat{}}}, 1272 }, 1273 want1: tok(token.EOF, ""), 1274 }, 1275 } 1276 for _, tt := range tests { 1277 t.Run(tt.name, func(t *testing.T) { 1278 p := &Parser{scanner: newTestScanner(tt.input)} 1279 got, got1 := p.Stat(p.Scan()) 1280 if !reflect.DeepEqual(got, tt.want) { 1281 t.Errorf("Parser.Stat() got = %v, want %v", got, tt.want) 1282 } 1283 if !reflect.DeepEqual(got1, tt.want1) { 1284 t.Errorf("Parser.Stat() got1 = %v, want %v", got1, tt.want1) 1285 } 1286 }) 1287 } 1288 } 1289 1290 func TestParseChunk(t *testing.T) { 1291 tests := []struct { 1292 name string 1293 input string 1294 wantStat ast.BlockStat 1295 wantErr bool 1296 }{ 1297 { 1298 name: "success", 1299 input: "return 1", 1300 wantStat: ast.NewBlockStat( 1301 nil, 1302 []ast.ExpNode{ast.NewInt(1)}, 1303 ), 1304 }, 1305 { 1306 name: "error", 1307 input: "return ?", 1308 wantErr: true, 1309 }, 1310 } 1311 for _, tt := range tests { 1312 t.Run(tt.name, func(t *testing.T) { 1313 gotStat, err := ParseChunk(newTestScanner(tt.input)) 1314 if (err != nil) != tt.wantErr { 1315 t.Errorf("ParseChunk() error = %v, wantErr %v", err, tt.wantErr) 1316 return 1317 } 1318 if !reflect.DeepEqual(gotStat, tt.wantStat) { 1319 t.Errorf("ParseChunk() = %v, want %v", gotStat, tt.wantStat) 1320 } 1321 }) 1322 } 1323 } 1324 1325 func TestParseExp(t *testing.T) { 1326 tests := []struct { 1327 name string 1328 input string 1329 wantExp ast.ExpNode 1330 wantErr bool 1331 }{ 1332 { 1333 name: "short expression", 1334 input: "-x.y", 1335 wantExp: &ast.UnOp{ 1336 Op: ops.OpNeg, 1337 Operand: ast.IndexExp{ 1338 Coll: name("x"), 1339 Idx: str("y"), 1340 }, 1341 }, 1342 }, 1343 { 1344 name: "short expression but with trailing bracket", 1345 input: "-x.y)", 1346 wantErr: true, 1347 }, 1348 } 1349 for _, tt := range tests { 1350 t.Run(tt.name, func(t *testing.T) { 1351 gotExp, err := ParseExp(newTestScanner(tt.input)) 1352 if (err != nil) != tt.wantErr { 1353 t.Errorf("ParseExp() error = %v, wantErr %v", err, tt.wantErr) 1354 return 1355 } 1356 if !reflect.DeepEqual(gotExp, tt.wantExp) { 1357 t.Errorf("ParseExp() = %v, want %v", gotExp, tt.wantExp) 1358 } 1359 }) 1360 } 1361 } 1362 1363 func TestError_Error(t *testing.T) { 1364 type fields struct { 1365 Got *token.Token 1366 Expected string 1367 } 1368 tests := []struct { 1369 name string 1370 fields fields 1371 want string 1372 }{ 1373 { 1374 name: "empty expected, type EOF", 1375 fields: fields{ 1376 Got: &token.Token{Type: token.EOF, Pos: token.Pos{Line: 1, Column: 2}}, 1377 }, 1378 want: "1:2: unexpected symbol near <eof>", 1379 }, 1380 { 1381 name: "'foo', expected, type non EOF", 1382 fields: fields{ 1383 Got: &token.Token{ 1384 Type: token.IDENT, 1385 Lit: []byte("bar"), 1386 Pos: token.Pos{Line: 10, Column: 3}, 1387 }, 1388 Expected: "'foo'", 1389 }, 1390 want: "10:3: expected 'foo' near 'bar'", 1391 }, 1392 } 1393 for _, tt := range tests { 1394 t.Run(tt.name, func(t *testing.T) { 1395 e := Error{ 1396 Got: tt.fields.Got, 1397 Expected: tt.fields.Expected, 1398 } 1399 if got := e.Error(); got != tt.want { 1400 t.Errorf("Error.Error() = %v, want %v", got, tt.want) 1401 } 1402 }) 1403 } 1404 }