github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/parser/parser_test.go (about)

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