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