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  }