cuelang.org/go@v0.13.0/cue/parser/parser_test.go (about)

     1  // Copyright 2018 The CUE Authors
     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  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"cuelang.org/go/cue/ast"
    23  	"cuelang.org/go/internal/astinternal"
    24  )
    25  
    26  func TestParse(t *testing.T) {
    27  	testCases := []struct{ desc, in, out string }{{
    28  
    29  		"ellipsis in structs",
    30  		`#Def: {
    31  			b: "2"
    32  			...
    33  		}
    34  		...
    35  
    36  		#Def2: {
    37  			...
    38  			b: "2"
    39  		}
    40  		#Def3: {...
    41  		_}
    42  		...
    43  		`,
    44  		`#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`,
    45  	}, {
    46  
    47  		"empty file", "", "",
    48  	}, {
    49  		"empty struct", "{}", "{}",
    50  	}, {
    51  		"empty structs", "{},{},", "{}, {}",
    52  	}, {
    53  		"empty structs; elided comma", "{}\n{}", "{}, {}",
    54  	}, {
    55  		"basic lits", `"a","b", 3,3.4,5,2_3`, `"a", "b", 3, 3.4, 5, 2_3`,
    56  	}, {
    57  		"keyword basic lits", `true,false,null,for,in,if,let,if`, `true, false, null, for, in, if, let, if`,
    58  	}, {
    59  		"keyword basic newline", `
    60  		true
    61  		false
    62  		null
    63  		for
    64  		in
    65  		if
    66  		let
    67  		if
    68  		`, `true, false, null, for, in, if, let, if`,
    69  	}, {
    70  		"keywords as labels",
    71  		`if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6
    72  		for: if: func: let: 3
    73  		`,
    74  		`if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, for: {if: {func: {let: 3}}}`,
    75  	}, {
    76  		"keywords as optional labels",
    77  		`if?: 0, for?: 1, in?: 2, where?: 3, div?: 4, quo?: 5, func?: 6
    78  		for?: if?: func?: let?: 3
    79  		`,
    80  		`if?: 0, for?: 1, in?: 2, where?: 3, div?: 4, quo?: 5, func?: 6, for?: {if?: {func?: {let?: 3}}}`,
    81  	}, {
    82  		"keywords as required labels",
    83  		`if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6
    84  		for!: if!: func!: let!: 3
    85  		`,
    86  		`if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6, for!: {if!: {func!: {let!: 3}}}`,
    87  	}, {
    88  		"keywords as alias",
    89  		`if=foo: 0
    90  		for=bar: 2
    91  		let=bar: 3
    92  		func=baz: 4
    93  		`,
    94  		`if=foo: 0, for=bar: 2, let=bar: 3, func=baz: 4`,
    95  	}, {
    96  		"keywords as selector",
    97  		`a : {
    98  			if: 0
    99  			for: 1
   100  			in: 2
   101  			where: 3
   102  			div: 4
   103  			quo: 5
   104  			func: 6
   105  			float: 7
   106  			null: if: func: let: 3
   107  		}, b: [
   108  			a.if,
   109  			a.for,
   110  			a.in,
   111  			a.where,
   112  			a.div,
   113  			a.quo,
   114  			a.func,
   115  			a.float,
   116  			a.null.if.func.let,
   117  		]`,
   118  		`a: {if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, float: 7, null: {if: {func: {let: 3}}}}, b: [a.if, a.for, a.in, a.where, a.div, a.quo, a.func, a.float, a.null.if.func.let]`,
   119  	}, {}, {
   120  		"json",
   121  		`{
   122  			"a": 1,
   123  			"b": "2",
   124  			"c": 3
   125  		}`,
   126  		`{"a": 1, "b": "2", "c": 3}`,
   127  	}, {
   128  		"json:extra comma",
   129  		`{
   130  			"a": 1,
   131  			"b": "2",
   132  			"c": 3,
   133  		}`,
   134  		`{"a": 1, "b": "2", "c": 3}`,
   135  	}, {
   136  		"json:simplified",
   137  		`{
   138  			a: 1
   139  			b: "2"
   140  			c: 3
   141  		}`,
   142  		`{a: 1, b: "2", c: 3}`,
   143  	}, {
   144  		"attributes",
   145  		`a: 1 @xml(,attr)
   146  		 b: 2 @foo(a,b=4) @go(Foo)
   147  		 c: {
   148  			 d: "x" @go(D) @json(,omitempty)
   149  			 e: "y" @ts(,type=string,"str")
   150  		 }`,
   151  		`a: 1 @xml(,attr), b: 2 @foo(a,b=4) @go(Foo), c: {d: "x" @go(D) @json(,omitempty), e: "y" @ts(,type=string,"str")}`,
   152  	}, {
   153  		"not emitted",
   154  		`a: true
   155  		 b?: "2"
   156  		 c?: 3
   157  
   158  		 d!: 2
   159  		 e: f!: 3
   160  
   161  		 "g\("en")"?: 4
   162  		 "h\("en")"!: 4
   163  		`,
   164  		`a: true, b?: "2", c?: 3, d!: 2, e: {f!: 3}, "g\("en")"?: 4, "h\("en")"!: 4`,
   165  	}, {
   166  		"definition",
   167  		`#Def: {
   168  			 b: "2"
   169  			 c: 3
   170  
   171  			 embedding
   172  		}
   173  		#Def: {}
   174  		`,
   175  		`#Def: {b: "2", c: 3, embedding}, #Def: {}`,
   176  	}, {
   177  		"one-line embedding",
   178  		`{ V1, V2 }`,
   179  		`{V1, V2}`,
   180  	}, {
   181  		"selectors",
   182  		`a.b. "str"`,
   183  		`a.b."str"`,
   184  	}, {
   185  		"selectors",
   186  		`a.b. "str"`,
   187  		`a.b."str"`,
   188  	}, {
   189  		"faulty bytes selector",
   190  		`a.b.'str'`,
   191  		"a.b._\nexpected selector, found 'STRING' 'str'",
   192  	}, {
   193  		"faulty multiline string selector",
   194  		`a.b."""
   195  			"""`,
   196  		"a.b._\nexpected selector, found 'STRING' \"\"\"\n\t\t\t\"\"\"",
   197  	}, {
   198  		"expression embedding",
   199  		`#Def: {
   200  			a.b.c
   201  			a > b < c
   202  			-1<2
   203  
   204  			foo: 2
   205  		}`,
   206  		`#Def: {a.b.c, a>b<c, -1<2, foo: 2}`,
   207  	}, {
   208  		"ellipsis in structs",
   209  		`#Def: {
   210  			b: "2"
   211  			...
   212  		}
   213  		...
   214  
   215  		#Def2: {
   216  			...
   217  			b: "2"
   218  		}
   219  		#Def3: {...
   220  		_}
   221  		...
   222  		`,
   223  		`#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`,
   224  	}, {
   225  		"emitted referencing non-emitted",
   226  		`a: 1
   227  		 b: "2"
   228  		 c: 3
   229  		{ name: b, total: a + b }`,
   230  		`a: 1, b: "2", c: 3, {name: b, total: a+b}`,
   231  	}, {
   232  		"package file",
   233  		`package k8s
   234  		 {}
   235  		`,
   236  		`package k8s, {}`,
   237  	}, {
   238  		"imports group",
   239  		`package k8s
   240  
   241  		import (
   242  			a "foo"
   243  			"bar/baz"
   244  		)
   245  		`,
   246  		`package k8s, import ( a "foo", "bar/baz" )`,
   247  	}, {
   248  		"imports single",
   249  		`package k8s
   250  
   251  		import a "foo"
   252  		import "bar/baz"
   253  			`,
   254  		`package k8s, import a "foo", import "bar/baz"`,
   255  	}, {
   256  		"collapsed fields",
   257  		`a: #b: c?: [Name=_]: d: 1
   258  		"g\("en")"?: 4
   259  		 // job foo { bar: 1 } // TODO error after foo
   260  		 job: "foo": [_]: { bar: 1 }
   261  		`,
   262  		`a: {#b: {c?: {[Name=_]: {d: 1}}}}, "g\("en")"?: 4, job: {"foo": {[_]: {bar: 1}}}`,
   263  	}, {
   264  		"identifiers",
   265  		`// 	$_: 1,
   266  			a: {b: {c: d}}
   267  			c: a
   268  			d: a.b
   269  			// e: a."b" // TODO: is an error
   270  			e: a.b.c
   271  			"f": f,
   272  			[X=_]: X
   273  		`,
   274  		"a: {b: {c: d}}, c: a, d: a.b, e: a.b.c, \"f\": f, [X=_]: X",
   275  	}, {
   276  		"predeclared identifiers",
   277  		`a:    __string
   278  		__int: 2`,
   279  		"a: __string, __int: 2\nidentifiers starting with '__' are reserved",
   280  	}, {
   281  		"empty fields",
   282  		`
   283  		"": 3
   284  		`,
   285  		`"": 3`,
   286  	}, {
   287  		"expressions",
   288  		`	a: (2 + 3) * 5
   289  			b: (2 + 3) + 4
   290  			c: 2 + 3 + 4
   291  			d: -1
   292  			e: !foo
   293  			f: _|_
   294  		`,
   295  		"a: (2+3)*5, b: (2+3)+4, c: 2+3+4, d: -1, e: !foo, f: _|_",
   296  	}, {
   297  		"pseudo keyword expressions",
   298  		`	a: (2 div 3) mod 5
   299  			b: (2 quo 3) rem 4
   300  			c: 2 div 3 div 4
   301  		`,
   302  		"a: (2 div 3) mod 5, b: (2 quo 3) rem 4, c: 2 div 3 div 4",
   303  	}, {
   304  		"ranges",
   305  		`	a: >=1 & <=2
   306  			b: >2.0  & <= 40.0
   307  			c: >"a" & <="b"
   308  			v: (>=1 & <=2) & <=(>=5 & <=10)
   309  			w: >1 & <=2 & <=3
   310  			d: >=3T & <=5M
   311  		`,
   312  		"a: >=1&<=2, b: >2.0&<=40.0, c: >\"a\"&<=\"b\", v: (>=1&<=2)&<=(>=5&<=10), w: >1&<=2&<=3, d: >=3T&<=5M",
   313  	}, {
   314  		"indices",
   315  		`{
   316  			a: b[2]
   317  			b: c[1:2]
   318  			c: "asdf"
   319  			d: c ["a"]
   320  		}`,
   321  		`{a: b[2], b: c[1:2], c: "asdf", d: c["a"]}`,
   322  	}, {
   323  		"calls",
   324  		`{
   325  			a: b(a.b, c.d)
   326  			b: a.b(c)
   327  		}`,
   328  		`{a: b(a.b, c.d), b: a.b(c)}`,
   329  	}, {
   330  		"lists",
   331  		`{
   332  			a: [ 1, 2, 3, b, c, ... ]
   333  			b: [ 1, 2, 3, ],
   334  			c: [ 1,
   335  			 2,
   336  			 3
   337  			 ],
   338  			d: [ 1+2, 2, 4,]
   339  		}`,
   340  		`{a: [1, 2, 3, b, c, ...], b: [1, 2, 3], c: [1, 2, 3], d: [1+2, 2, 4]}`,
   341  	}, {
   342  		"list types",
   343  		`{
   344  			a: 4*[int]
   345  			b: <=5*[{a: 5}]
   346  			c1: [...int]
   347  			c2: [...]
   348  			c3: [1, 2, ...int,]
   349  		}`,
   350  		`{a: 4*[int], b: <=5*[{a: 5}], c1: [...int], c2: [...], c3: [1, 2, ...int]}`,
   351  	}, {
   352  		"list comprehensions",
   353  		`{
   354  				y: [1,2,3]
   355  				b: [for x in y if x == 1 { x }],
   356  			}`,
   357  		`{y: [1, 2, 3], b: [for x in y if x==1 {x}]}`,
   358  	}, {
   359  		"field comprehensions",
   360  		`{
   361  				y: { a: 1, b: 2}
   362  				a: {
   363  					for k, v in y if v > 2 {
   364  						"\(k)": v
   365  					}
   366  				}
   367  			 }`,
   368  		`{y: {a: 1, b: 2}, a: {for k: v in y if v>2 {"\(k)": v}}}`,
   369  	}, {
   370  		"nested comprehensions",
   371  		`{
   372  			y: { a: 1, b: 2}
   373  			a: {
   374  				for k, v in y let x = v+2 if x > 2 {
   375  					"\(k)": v
   376  				}
   377  			}
   378  		}`,
   379  		`{y: {a: 1, b: 2}, a: {for k: v in y let x=v+2 if x>2 {"\(k)": v}}}`,
   380  	}, {
   381  		"let declaration",
   382  		`{
   383  			let X = 42
   384  			let Y = "42",
   385  			let Z = 10 + 12
   386  		}`,
   387  		`{let X=42, let Y="42", let Z=10+12}`,
   388  	}, {
   389  		"duplicates allowed",
   390  		`{
   391  			a: b: 3
   392  			a: { b: 3 }
   393  		}`,
   394  		"{a: {b: 3}, a: {b: 3}}",
   395  	}, {
   396  		"templates", // TODO: remove
   397  		`{
   398  			[foo=_]: { a: int }
   399  			a:     { a: 1 }
   400  		}`,
   401  		"{[foo=_]: {a: int}, a: {a: 1}}",
   402  	}, {
   403  		"value alias",
   404  		`
   405  		{
   406  			a: X=foo
   407  			b: Y={foo}
   408  			c: d: e: X=5
   409  		}
   410  		`,
   411  		`{a: X=foo, b: Y={foo}, c: {d: {e: X=5}}}`,
   412  	}, {
   413  		"dynamic labels",
   414  		`{
   415  			(x): a: int
   416  			x:   "foo"
   417  			a: {
   418  				(a.b)
   419  			}
   420  
   421  			(x)?: 1
   422  			y: (x)!: 2
   423  		}`,
   424  		`{(x): {a: int}, x: "foo", a: {(a.b)}, (x)?: 1, y: {(x)!: 2}}`,
   425  	}, {
   426  		"foo",
   427  		`[
   428  			[1],
   429  			[1, 2],
   430  			[1, 2, 3],
   431  		]`,
   432  		"[[1], [1, 2], [1, 2, 3]]",
   433  	}, {
   434  		"interpolation",
   435  		`a: "foo \(ident)"
   436  		 b: "bar \(bar)  $$$ "
   437  		 c: "nest \(   { a: "\( nest ) "}.a ) \(5)"
   438  		 m1: """
   439  			 multi \(bar)
   440  			 """
   441  		 m2: '''
   442  			 \(bar) multi
   443  			 '''`,
   444  		`a: "foo \(ident)", b: "bar \(bar)  $$$ ", c: "nest \({a: "\(nest) "}.a) \(5)", ` + "m1: \"\"\"\n\t\t\t multi \\(bar)\n\t\t\t \"\"\", m2: '''\n\t\t\t \\(bar) multi\n\t\t\t '''",
   445  	}, {
   446  		"file comments",
   447  		`// foo
   448  
   449  		// uni
   450  		package foo // uniline
   451  
   452  		// file.1
   453  		// file.2
   454  
   455  		`,
   456  		"<[0// foo] <[d0// uni] [l3// uniline] [3// file.1 // file.2] package foo>>",
   457  	}, {
   458  		"line comments",
   459  		`// doc
   460  		 a: 5 // line
   461  		 b: 6 // lineb
   462  			  // next
   463  			`, // next is followed by EOF. Ensure it doesn't move to file.
   464  		"<[d0// doc] [l5// line] a: 5>, " +
   465  			"<[l5// lineb] [5// next] b: 6>",
   466  	}, {
   467  		"alt comments",
   468  		`// a ...
   469  		a: 5 // line a
   470  
   471  		// about a
   472  
   473  		// b ...
   474  		b: // lineb
   475  		  6
   476  
   477  		// about b
   478  
   479  		c: 7
   480  
   481  		// about c
   482  
   483  		// about d
   484  		d:
   485  			// about e
   486  			e: 3
   487  		`,
   488  		"<[d0// a ...] [l5// line a] [5// about a] a: 5>, " +
   489  			"<[d0// b ...] [l2// lineb] [5// about b] b: 6>, " +
   490  			"<[5// about c] c: 7>, " +
   491  			"<[d0// about d] d: {<[d0// about e] e>: 3}>",
   492  	}, {
   493  		"expr comments",
   494  		`
   495  		a: 2 +  // 2 +
   496  		   3 +  // 3 +
   497  		   4    // 4
   498  		   `,
   499  		"<[l5// 4] a: <[l2// 3 +] <[l2// 2 +] 2+3>+4>>",
   500  	}, {
   501  		"composit comments",
   502  		`a : {
   503  			a: 1, b: 2, c: 3, d: 4
   504  			// end
   505  		}
   506  		b: [
   507  			1, 2, 3, 4, 5,
   508  			// end
   509  		]
   510  		c: [ 1, 2, 3, 4, // here
   511  			{ a: 3 }, // here
   512  			5, 6, 7, 8 // and here
   513  		]
   514  		d: {
   515  			a: 1 // Hello
   516  			// Doc
   517  			b: 2
   518  		}
   519  		e1: [
   520  			// comment in list body
   521  		]
   522  		e2: {
   523  			// comment in struct body
   524  		}
   525  		`,
   526  		"a: {a: 1, b: 2, c: 3, <[d5// end] d: 4>}, " +
   527  			"b: [1, 2, 3, 4, <[d2// end] 5>], " +
   528  			"c: [1, 2, 3, <[l2// here] 4>, <[l4// here] {a: 3}>, 5, 6, 7, <[l2// and here] 8>], " +
   529  			"d: {<[l5// Hello] a: 1>, <[d0// Doc] b: 2>}, " +
   530  			"e1: <[d1// comment in list body] []>, " +
   531  			"e2: <[d1// comment in struct body] {}>",
   532  	}, {
   533  		"attribute comments",
   534  		`
   535  		a: 1 @a() @b() // d
   536  		`,
   537  		`<[l5// d] a: 1 @a() @b()>`,
   538  	}, {
   539  		"attribute declarations",
   540  		`
   541  		@foo()
   542  
   543  		package bar
   544  
   545  		@bar()
   546  
   547  		import "strings"
   548  
   549  		@baz()
   550  			`,
   551  		`@foo(), package bar, @bar(), import "strings", @baz()`,
   552  	}, {
   553  		"comprehension comments",
   554  		`
   555  		if X {
   556  			// Comment 1
   557  			Field: 2
   558  			// Comment 2
   559  		}
   560  		`,
   561  		`if X <[d2// Comment 2] {<[d0// Comment 1] Field: 2>}>`,
   562  	}, {
   563  		"let comments",
   564  		`let X = foo // Comment 1`,
   565  		`<[5// Comment 1] let X=foo>`,
   566  	}, {
   567  		"emit comments",
   568  		`// a comment at the beginning of the file
   569  
   570  		// a second comment
   571  
   572  		// comment
   573  		a: 5
   574  
   575  		{}
   576  
   577  		// a comment at the end of the file
   578  		`,
   579  		"<[0// a comment at the beginning of the file] [0// a second comment] <[d0// comment] a: 5>, <[2// a comment at the end of the file] {}>>",
   580  	}, {
   581  		"composite comments 2",
   582  		`
   583  	{
   584  // foo
   585  
   586  // fooo
   587  foo: 1
   588  
   589  bar: 2
   590  	}
   591  
   592  [
   593  	{"name": "value"}, // each element has a long
   594  	{"name": "next"}   // optional next element
   595  ]
   596  `,
   597  		`{<[0// foo] [d0// fooo] foo: 1>, bar: 2}, [<[l4// each element has a long] {"name": "value"}>, <[l4// optional next element] {"name": "next"}>]`,
   598  	}, {
   599  		desc: "field aliasing",
   600  		in: `
   601  		I="\(k)": v
   602  		S="foo-bar": w
   603  		L=foo: x
   604  		X=[0]: {
   605  			foo: X | null
   606  		}
   607  		[Y=string]: { name: Y }
   608  		X1=[X2=<"d"]: { name: X2 }
   609  		Y1=foo: Y2=bar: [Y1, Y2]
   610  		`,
   611  		out: `I="\(k)": v, ` +
   612  			`S="foo-bar": w, ` +
   613  			`L=foo: x, ` +
   614  			`X=[0]: {foo: X|null}, ` +
   615  			`[Y=string]: {name: Y}, ` +
   616  			`X1=[X2=<"d"]: {name: X2}, ` +
   617  			`Y1=foo: {Y2=bar: [Y1, Y2]}`,
   618  	}, {
   619  		desc: "allow keyword in expression",
   620  		in: `
   621  		foo: in & 2
   622  		`,
   623  		out: "foo: in&2",
   624  	}, {
   625  		desc: "dot import",
   626  		in: `
   627  		import . "foo"
   628  		`,
   629  		out: "import , \"foo\"\nexpected 'STRING', found '.'",
   630  	}, {
   631  		desc: "attributes",
   632  		in: `
   633  		package name
   634  
   635  		@t1(v1)
   636  
   637  		{
   638  			@t2(v2)
   639  		}
   640  		a: {
   641  			a: 1
   642  			@t3(v3)
   643  			@t4(v4)
   644  			c: 2
   645  		}
   646  		`,
   647  		out: "package name, @t1(v1), {@t2(v2)}, a: {a: 1, @t3(v3), @t4(v4), c: 2}",
   648  	}, {
   649  		desc: "Issue #276",
   650  		in: `
   651  		a: int=>2
   652  		`,
   653  		out: "a: int=>2",
   654  	}, {
   655  		desc: "struct comments",
   656  		in: `
   657  		struct: {
   658  			// This is a comment
   659  
   660  			// This is a comment
   661  
   662  			// Another comment
   663  			something: {
   664  			}
   665  
   666  			// extra comment
   667  		}`,
   668  		out: `struct: {<[0// This is a comment] [0// This is a comment] [d0// Another comment] [d5// extra comment] something: {}>}`,
   669  	}, {
   670  		desc: "list comments",
   671  		in: `
   672  		list: [
   673  			// Comment1
   674  
   675  			// Comment2
   676  
   677  			// Another comment
   678  			{
   679  			},
   680  
   681  			// Comment 3
   682  		]`,
   683  		out: "list: [<[0// Comment1] [0// Comment2] [d0// Another comment] [d3// Comment 3] {}>]",
   684  	}, {
   685  		desc: "call comments",
   686  		in: `
   687  		funcArg1: foo(
   688  			{},
   689  
   690  			// Comment1
   691  
   692  			// Comment2
   693  			{}
   694  
   695  			// Comment3
   696  		)`,
   697  		out: "funcArg1: foo(<[1// Comment1] {}>, <[d0// Comment2] [d1// Comment3] {}>)",
   698  	}, {
   699  		desc: "front-style commas",
   700  		in: `
   701  			frontStyle: { "key": "value"
   702  				, "key2": "value2"
   703  				, "foo" : bar
   704  			}
   705  			`,
   706  		out: "frontStyle: {\"key\": \"value\", \"key2\": \"value2\", \"foo\": bar}",
   707  	}, {
   708  		desc: "function types",
   709  		in: `
   710  			f0: func(): int
   711  			f1: func(int): int
   712  			f2: func(int, string): int
   713  			f3: func({a: int, b: string}): bool
   714  			f4: func(bool, func(int, string): int): string
   715  			f5: func(int, int): func(bool, bool): bool
   716  			f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int
   717  		`,
   718  		out: "f0: func(): int, f1: func(int): int, f2: func(int, string): int, f3: func({a: int, b: string}): bool, f4: func(bool, func(int, string): int): string, f5: func(int, int): func(bool, bool): bool, f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int",
   719  	}}
   720  	for _, tc := range testCases {
   721  		t.Run(tc.desc, func(t *testing.T) {
   722  			mode := []Option{AllErrors}
   723  			if strings.Contains(tc.desc, "comments") {
   724  				mode = append(mode, ParseComments)
   725  			}
   726  			if strings.Contains(tc.desc, "function") {
   727  				mode = append(mode, ParseFuncs)
   728  			}
   729  			f, err := ParseFile("input", tc.in, mode...)
   730  			got := astinternal.DebugStr(f)
   731  			if err != nil {
   732  				got += "\n" + err.Error()
   733  			}
   734  			if got != tc.out {
   735  				t.Errorf("\ngot  %q;\nwant %q", got, tc.out)
   736  			}
   737  		})
   738  	}
   739  }
   740  
   741  func TestStrict(t *testing.T) {
   742  	testCases := []struct{ desc, in string }{
   743  		{"block comments",
   744  			`a: 1 /* a */`},
   745  		{"space separator",
   746  			`a b c: 2`},
   747  		{"reserved identifiers",
   748  			`__foo: 3`},
   749  		{"old-style alias 1",
   750  			`X=3`},
   751  		{"old-style alias 2",
   752  			`X={}`},
   753  
   754  		// Not yet supported
   755  		{"additional typed not yet supported",
   756  			`{...int}`},
   757  	}
   758  	for _, tc := range testCases {
   759  		t.Run(tc.desc, func(t *testing.T) {
   760  			mode := []Option{AllErrors, ParseComments, FromVersion(Latest)}
   761  			_, err := ParseFile("input", tc.in, mode...)
   762  			if err == nil {
   763  				t.Errorf("unexpected success: %v", tc.in)
   764  			}
   765  		})
   766  	}
   767  }
   768  
   769  func TestParseExpr(t *testing.T) {
   770  	// just kicking the tires:
   771  	// a valid arithmetic expression
   772  	src := "a + b"
   773  	x, err := parseExprString(src)
   774  	if err != nil {
   775  		t.Errorf("ParseExpr(%q): %v", src, err)
   776  	}
   777  	// sanity check
   778  	if _, ok := x.(*ast.BinaryExpr); !ok {
   779  		t.Errorf("ParseExpr(%q): got %T, want *BinaryExpr", src, x)
   780  	}
   781  
   782  	// an invalid expression
   783  	src = "a + *"
   784  	if _, err := parseExprString(src); err == nil {
   785  		t.Errorf("ParseExpr(%q): got no error", src)
   786  	}
   787  
   788  	// a comma is not permitted unless automatically inserted
   789  	src = "a + b\n"
   790  	if _, err := parseExprString(src); err != nil {
   791  		t.Errorf("ParseExpr(%q): got error %s", src, err)
   792  	}
   793  	src = "a + b;"
   794  	if _, err := parseExprString(src); err == nil {
   795  		t.Errorf("ParseExpr(%q): got no error", src)
   796  	}
   797  
   798  	// check resolution
   799  	src = "{ foo: bar, bar: foo }"
   800  	x, err = parseExprString(src)
   801  	if err != nil {
   802  		t.Fatalf("ParseExpr(%q): %v", src, err)
   803  	}
   804  	for _, d := range x.(*ast.StructLit).Elts {
   805  		v := d.(*ast.Field).Value.(*ast.Ident)
   806  		if v.Scope == nil {
   807  			t.Errorf("ParseExpr(%q): scope of field %v not set", src, v.Name)
   808  		}
   809  		if v.Node == nil {
   810  			t.Errorf("ParseExpr(%q): scope of node %v not set", src, v.Name)
   811  		}
   812  	}
   813  
   814  	// various other stuff following a valid expression
   815  	const validExpr = "a + b"
   816  	const anything = "dh3*#D)#_"
   817  	for _, c := range "!)]};," {
   818  		src := validExpr + string(c) + anything
   819  		if _, err := parseExprString(src); err == nil {
   820  			t.Errorf("ParseExpr(%q): got no error", src)
   821  		}
   822  	}
   823  
   824  	// ParseExpr must not crash
   825  	for _, src := range valids {
   826  		_, _ = parseExprString(src)
   827  	}
   828  }
   829  
   830  func TestImports(t *testing.T) {
   831  	var imports = map[string]bool{
   832  		`"a"`:        true,
   833  		`"a/b"`:      true,
   834  		`"a.b"`:      true,
   835  		`'m\x61th'`:  true,
   836  		`"greek/αβ"`: true,
   837  		`""`:         false,
   838  
   839  		// Each of these pairs tests both #""# vs "" strings
   840  		// and also use of invalid characters spelled out as
   841  		// escape sequences and written directly.
   842  		// For example `"\x00"` tests import "\x00"
   843  		// while "`\x00`" tests import `<actual-NUL-byte>`.
   844  		`#"a"#`:        true,
   845  		`"\x00"`:       false,
   846  		"'\x00'":       false,
   847  		`"\x7f"`:       false,
   848  		"`\x7f`":       false,
   849  		`"a!"`:         false,
   850  		"#'a!'#":       false,
   851  		`"a b"`:        false,
   852  		`#"a b"#`:      false,
   853  		`"a\\b"`:       false,
   854  		"#\"a\\b\"#":   false,
   855  		"\"`a`\"":      false,
   856  		"#'\"a\"'#":    false,
   857  		`"\x80\x80"`:   false,
   858  		"#'\x80\x80'#": false,
   859  		`"\xFFFD"`:     false,
   860  		"#'\xFFFD'#":   false,
   861  	}
   862  	for path, isValid := range imports {
   863  		t.Run(path, func(t *testing.T) {
   864  			src := fmt.Sprintf("package p, import %s", path)
   865  			_, err := ParseFile("", src)
   866  			switch {
   867  			case err != nil && isValid:
   868  				t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
   869  			case err == nil && !isValid:
   870  				t.Errorf("ParseFile(%s): got no error; expected one", src)
   871  			}
   872  		})
   873  	}
   874  }
   875  
   876  // TestIncompleteSelection ensures that an incomplete selector
   877  // expression is parsed as a (blank) *SelectorExpr, not a
   878  // *BadExpr.
   879  func TestIncompleteSelection(t *testing.T) {
   880  	for _, src := range []string{
   881  		"{ a: fmt. }",         // at end of object
   882  		"{ a: fmt.\n0.0: x }", // not at end of struct
   883  	} {
   884  		t.Run("", func(t *testing.T) {
   885  			f, err := ParseFile("", src)
   886  			if err == nil {
   887  				t.Fatalf("ParseFile(%s) succeeded unexpectedly", src)
   888  			}
   889  
   890  			const wantErr = "expected selector"
   891  			if !strings.Contains(err.Error(), wantErr) {
   892  				t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
   893  			}
   894  
   895  			var sel *ast.SelectorExpr
   896  			ast.Walk(f, func(n ast.Node) bool {
   897  				if n, ok := n.(*ast.SelectorExpr); ok {
   898  					sel = n
   899  				}
   900  				return true
   901  			}, nil)
   902  			if sel == nil {
   903  				t.Fatalf("found no *SelectorExpr: %#v %s", f.Decls[0], astinternal.DebugStr(f))
   904  			}
   905  			const wantSel = "&{fmt _ {<nil>} {{}}}"
   906  			if fmt.Sprint(sel) != wantSel {
   907  				t.Fatalf("found selector %v, want %s", sel, wantSel)
   908  			}
   909  		})
   910  	}
   911  }
   912  
   913  // For debugging, do not delete.
   914  func TestX(t *testing.T) {
   915  	t.Skip()
   916  
   917  	f, err := ParseFile("input", `
   918  	`, ParseComments)
   919  	if err != nil {
   920  		t.Errorf("unexpected error: %v", err)
   921  	}
   922  	t.Error(astinternal.DebugStr(f))
   923  }