• 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	"testing"
21	"text/scanner"
22)
23
24func mkpos(offset, line, column int) scanner.Position {
25	return scanner.Position{
26		Offset: offset,
27		Line:   line,
28		Column: column,
29	}
30}
31
32var validParseTestCases = []struct {
33	input    string
34	defs     []Definition
35	comments []*CommentGroup
36}{
37	{`
38		foo {}
39		`,
40		[]Definition{
41			&Module{
42				Type:    "foo",
43				TypePos: mkpos(3, 2, 3),
44				Map: Map{
45					LBracePos: mkpos(7, 2, 7),
46					RBracePos: mkpos(8, 2, 8),
47				},
48			},
49		},
50		nil,
51	},
52
53	{`
54		foo {
55			name: "abc",
56		}
57		`,
58		[]Definition{
59			&Module{
60				Type:    "foo",
61				TypePos: mkpos(3, 2, 3),
62				Map: Map{
63					LBracePos: mkpos(7, 2, 7),
64					RBracePos: mkpos(27, 4, 3),
65					Properties: []*Property{
66						{
67							Name:     "name",
68							NamePos:  mkpos(12, 3, 4),
69							ColonPos: mkpos(16, 3, 8),
70							Value: &String{
71								LiteralPos: mkpos(18, 3, 10),
72								Value:      "abc",
73							},
74						},
75					},
76				},
77			},
78		},
79		nil,
80	},
81
82	{`
83		foo {
84			isGood: true,
85		}
86		`,
87		[]Definition{
88			&Module{
89				Type:    "foo",
90				TypePos: mkpos(3, 2, 3),
91				Map: Map{
92					LBracePos: mkpos(7, 2, 7),
93					RBracePos: mkpos(28, 4, 3),
94					Properties: []*Property{
95						{
96							Name:     "isGood",
97							NamePos:  mkpos(12, 3, 4),
98							ColonPos: mkpos(18, 3, 10),
99							Value: &Bool{
100								LiteralPos: mkpos(20, 3, 12),
101								Value:      true,
102							},
103						},
104					},
105				},
106			},
107		},
108		nil,
109	},
110
111	{`
112		foo {
113			stuff: ["asdf", "jkl;", "qwert",
114				"uiop", "bnm,"]
115		}
116		`,
117		[]Definition{
118			&Module{
119				Type:    "foo",
120				TypePos: mkpos(3, 2, 3),
121				Map: Map{
122					LBracePos: mkpos(7, 2, 7),
123					RBracePos: mkpos(67, 5, 3),
124					Properties: []*Property{
125						{
126							Name:     "stuff",
127							NamePos:  mkpos(12, 3, 4),
128							ColonPos: mkpos(17, 3, 9),
129							Value: &List{
130								LBracePos: mkpos(19, 3, 11),
131								RBracePos: mkpos(63, 4, 19),
132								Values: []Expression{
133									&String{
134										LiteralPos: mkpos(20, 3, 12),
135										Value:      "asdf",
136									},
137									&String{
138										LiteralPos: mkpos(28, 3, 20),
139										Value:      "jkl;",
140									},
141									&String{
142										LiteralPos: mkpos(36, 3, 28),
143										Value:      "qwert",
144									},
145									&String{
146										LiteralPos: mkpos(49, 4, 5),
147										Value:      "uiop",
148									},
149									&String{
150										LiteralPos: mkpos(57, 4, 13),
151										Value:      "bnm,",
152									},
153								},
154							},
155						},
156					},
157				},
158			},
159		},
160		nil,
161	},
162
163	{`
164		foo {
165			stuff: {
166				isGood: true,
167				name: "bar"
168			}
169		}
170		`,
171		[]Definition{
172			&Module{
173				Type:    "foo",
174				TypePos: mkpos(3, 2, 3),
175				Map: Map{
176					LBracePos: mkpos(7, 2, 7),
177					RBracePos: mkpos(62, 7, 3),
178					Properties: []*Property{
179						{
180							Name:     "stuff",
181							NamePos:  mkpos(12, 3, 4),
182							ColonPos: mkpos(17, 3, 9),
183							Value: &Map{
184								LBracePos: mkpos(19, 3, 11),
185								RBracePos: mkpos(58, 6, 4),
186								Properties: []*Property{
187									{
188										Name:     "isGood",
189										NamePos:  mkpos(25, 4, 5),
190										ColonPos: mkpos(31, 4, 11),
191										Value: &Bool{
192											LiteralPos: mkpos(33, 4, 13),
193											Value:      true,
194										},
195									},
196									{
197										Name:     "name",
198										NamePos:  mkpos(43, 5, 5),
199										ColonPos: mkpos(47, 5, 9),
200										Value: &String{
201											LiteralPos: mkpos(49, 5, 11),
202											Value:      "bar",
203										},
204									},
205								},
206							},
207						},
208					},
209				},
210			},
211		},
212		nil,
213	},
214
215	{`
216		// comment1
217		foo /* test */ {
218			// comment2
219			isGood: true,  // comment3
220		}
221		`,
222		[]Definition{
223			&Module{
224				Type:    "foo",
225				TypePos: mkpos(17, 3, 3),
226				Map: Map{
227					LBracePos: mkpos(32, 3, 18),
228					RBracePos: mkpos(81, 6, 3),
229					Properties: []*Property{
230						{
231							Name:     "isGood",
232							NamePos:  mkpos(52, 5, 4),
233							ColonPos: mkpos(58, 5, 10),
234							Value: &Bool{
235								LiteralPos: mkpos(60, 5, 12),
236								Value:      true,
237							},
238						},
239					},
240				},
241			},
242		},
243		[]*CommentGroup{
244			{
245				Comments: []*Comment{
246					&Comment{
247						Comment: []string{"// comment1"},
248						Slash:   mkpos(3, 2, 3),
249					},
250				},
251			},
252			{
253				Comments: []*Comment{
254					&Comment{
255						Comment: []string{"/* test */"},
256						Slash:   mkpos(21, 3, 7),
257					},
258				},
259			},
260			{
261				Comments: []*Comment{
262					&Comment{
263						Comment: []string{"// comment2"},
264						Slash:   mkpos(37, 4, 4),
265					},
266				},
267			},
268			{
269				Comments: []*Comment{
270					&Comment{
271						Comment: []string{"// comment3"},
272						Slash:   mkpos(67, 5, 19),
273					},
274				},
275			},
276		},
277	},
278
279	{`
280		foo {
281			name: "abc",
282		}
283
284		bar {
285			name: "def",
286		}
287		`,
288		[]Definition{
289			&Module{
290				Type:    "foo",
291				TypePos: mkpos(3, 2, 3),
292				Map: Map{
293					LBracePos: mkpos(7, 2, 7),
294					RBracePos: mkpos(27, 4, 3),
295					Properties: []*Property{
296						{
297							Name:     "name",
298							NamePos:  mkpos(12, 3, 4),
299							ColonPos: mkpos(16, 3, 8),
300							Value: &String{
301								LiteralPos: mkpos(18, 3, 10),
302								Value:      "abc",
303							},
304						},
305					},
306				},
307			},
308			&Module{
309				Type:    "bar",
310				TypePos: mkpos(32, 6, 3),
311				Map: Map{
312					LBracePos: mkpos(36, 6, 7),
313					RBracePos: mkpos(56, 8, 3),
314					Properties: []*Property{
315						{
316							Name:     "name",
317							NamePos:  mkpos(41, 7, 4),
318							ColonPos: mkpos(45, 7, 8),
319							Value: &String{
320								LiteralPos: mkpos(47, 7, 10),
321								Value:      "def",
322							},
323						},
324					},
325				},
326			},
327		},
328		nil,
329	},
330	{`
331		foo = "stuff"
332		bar = foo
333		baz = foo + bar
334		boo = baz
335		boo += foo
336		`,
337		[]Definition{
338			&Assignment{
339				Name:      "foo",
340				NamePos:   mkpos(3, 2, 3),
341				EqualsPos: mkpos(7, 2, 7),
342				Value: &String{
343					LiteralPos: mkpos(9, 2, 9),
344					Value:      "stuff",
345				},
346				OrigValue: &String{
347					LiteralPos: mkpos(9, 2, 9),
348					Value:      "stuff",
349				},
350				Assigner:   "=",
351				Referenced: true,
352			},
353			&Assignment{
354				Name:      "bar",
355				NamePos:   mkpos(19, 3, 3),
356				EqualsPos: mkpos(23, 3, 7),
357				Value: &Variable{
358					Name:    "foo",
359					NamePos: mkpos(25, 3, 9),
360					Value: &String{
361						LiteralPos: mkpos(9, 2, 9),
362						Value:      "stuff",
363					},
364				},
365				OrigValue: &Variable{
366					Name:    "foo",
367					NamePos: mkpos(25, 3, 9),
368					Value: &String{
369						LiteralPos: mkpos(9, 2, 9),
370						Value:      "stuff",
371					},
372				},
373				Assigner:   "=",
374				Referenced: true,
375			},
376			&Assignment{
377				Name:      "baz",
378				NamePos:   mkpos(31, 4, 3),
379				EqualsPos: mkpos(35, 4, 7),
380				Value: &Operator{
381					OperatorPos: mkpos(41, 4, 13),
382					Operator:    '+',
383					Value: &String{
384						LiteralPos: mkpos(9, 2, 9),
385						Value:      "stuffstuff",
386					},
387					Args: [2]Expression{
388						&Variable{
389							Name:    "foo",
390							NamePos: mkpos(37, 4, 9),
391							Value: &String{
392								LiteralPos: mkpos(9, 2, 9),
393								Value:      "stuff",
394							},
395						},
396						&Variable{
397							Name:    "bar",
398							NamePos: mkpos(43, 4, 15),
399							Value: &Variable{
400								Name:    "foo",
401								NamePos: mkpos(25, 3, 9),
402								Value: &String{
403									LiteralPos: mkpos(9, 2, 9),
404									Value:      "stuff",
405								},
406							},
407						},
408					},
409				},
410				OrigValue: &Operator{
411					OperatorPos: mkpos(41, 4, 13),
412					Operator:    '+',
413					Value: &String{
414						LiteralPos: mkpos(9, 2, 9),
415						Value:      "stuffstuff",
416					},
417					Args: [2]Expression{
418						&Variable{
419							Name:    "foo",
420							NamePos: mkpos(37, 4, 9),
421							Value: &String{
422								LiteralPos: mkpos(9, 2, 9),
423								Value:      "stuff",
424							},
425						},
426						&Variable{
427							Name:    "bar",
428							NamePos: mkpos(43, 4, 15),
429							Value: &Variable{
430								Name:    "foo",
431								NamePos: mkpos(25, 3, 9),
432								Value: &String{
433									LiteralPos: mkpos(9, 2, 9),
434									Value:      "stuff",
435								},
436							},
437						},
438					},
439				},
440				Assigner:   "=",
441				Referenced: true,
442			},
443			&Assignment{
444				Name:      "boo",
445				NamePos:   mkpos(49, 5, 3),
446				EqualsPos: mkpos(53, 5, 7),
447				Value: &Operator{
448					Args: [2]Expression{
449						&Variable{
450							Name:    "baz",
451							NamePos: mkpos(55, 5, 9),
452							Value: &Operator{
453								OperatorPos: mkpos(41, 4, 13),
454								Operator:    '+',
455								Value: &String{
456									LiteralPos: mkpos(9, 2, 9),
457									Value:      "stuffstuff",
458								},
459								Args: [2]Expression{
460									&Variable{
461										Name:    "foo",
462										NamePos: mkpos(37, 4, 9),
463										Value: &String{
464											LiteralPos: mkpos(9, 2, 9),
465											Value:      "stuff",
466										},
467									},
468									&Variable{
469										Name:    "bar",
470										NamePos: mkpos(43, 4, 15),
471										Value: &Variable{
472											Name:    "foo",
473											NamePos: mkpos(25, 3, 9),
474											Value: &String{
475												LiteralPos: mkpos(9, 2, 9),
476												Value:      "stuff",
477											},
478										},
479									},
480								},
481							},
482						},
483						&Variable{
484							Name:    "foo",
485							NamePos: mkpos(68, 6, 10),
486							Value: &String{
487								LiteralPos: mkpos(9, 2, 9),
488								Value:      "stuff",
489							},
490						},
491					},
492					OperatorPos: mkpos(66, 6, 8),
493					Operator:    '+',
494					Value: &String{
495						LiteralPos: mkpos(9, 2, 9),
496						Value:      "stuffstuffstuff",
497					},
498				},
499				OrigValue: &Variable{
500					Name:    "baz",
501					NamePos: mkpos(55, 5, 9),
502					Value: &Operator{
503						OperatorPos: mkpos(41, 4, 13),
504						Operator:    '+',
505						Value: &String{
506							LiteralPos: mkpos(9, 2, 9),
507							Value:      "stuffstuff",
508						},
509						Args: [2]Expression{
510							&Variable{
511								Name:    "foo",
512								NamePos: mkpos(37, 4, 9),
513								Value: &String{
514									LiteralPos: mkpos(9, 2, 9),
515									Value:      "stuff",
516								},
517							},
518							&Variable{
519								Name:    "bar",
520								NamePos: mkpos(43, 4, 15),
521								Value: &Variable{
522									Name:    "foo",
523									NamePos: mkpos(25, 3, 9),
524									Value: &String{
525										LiteralPos: mkpos(9, 2, 9),
526										Value:      "stuff",
527									},
528								},
529							},
530						},
531					},
532				},
533				Assigner: "=",
534			},
535			&Assignment{
536				Name:      "boo",
537				NamePos:   mkpos(61, 6, 3),
538				EqualsPos: mkpos(66, 6, 8),
539				Value: &Variable{
540					Name:    "foo",
541					NamePos: mkpos(68, 6, 10),
542					Value: &String{
543						LiteralPos: mkpos(9, 2, 9),
544						Value:      "stuff",
545					},
546				},
547				OrigValue: &Variable{
548					Name:    "foo",
549					NamePos: mkpos(68, 6, 10),
550					Value: &String{
551						LiteralPos: mkpos(9, 2, 9),
552						Value:      "stuff",
553					},
554				},
555				Assigner: "+=",
556			},
557		},
558		nil,
559	},
560	{`
561		// comment1
562		// comment2
563
564		/* comment3
565		   comment4 */
566		// comment5
567
568		/* comment6 */ /* comment7 */ // comment8
569		`,
570		nil,
571		[]*CommentGroup{
572			{
573				Comments: []*Comment{
574					&Comment{
575						Comment: []string{"// comment1"},
576						Slash:   mkpos(3, 2, 3),
577					},
578					&Comment{
579						Comment: []string{"// comment2"},
580						Slash:   mkpos(17, 3, 3),
581					},
582				},
583			},
584			{
585				Comments: []*Comment{
586					&Comment{
587						Comment: []string{"/* comment3", "		   comment4 */"},
588						Slash: mkpos(32, 5, 3),
589					},
590					&Comment{
591						Comment: []string{"// comment5"},
592						Slash:   mkpos(63, 7, 3),
593					},
594				},
595			},
596			{
597				Comments: []*Comment{
598					&Comment{
599						Comment: []string{"/* comment6 */"},
600						Slash:   mkpos(78, 9, 3),
601					},
602					&Comment{
603						Comment: []string{"/* comment7 */"},
604						Slash:   mkpos(93, 9, 18),
605					},
606					&Comment{
607						Comment: []string{"// comment8"},
608						Slash:   mkpos(108, 9, 33),
609					},
610				},
611			},
612		},
613	},
614}
615
616func TestParseValidInput(t *testing.T) {
617	for _, testCase := range validParseTestCases {
618		r := bytes.NewBufferString(testCase.input)
619		file, errs := ParseAndEval("", r, NewScope(nil))
620		if len(errs) != 0 {
621			t.Errorf("test case: %s", testCase.input)
622			t.Errorf("unexpected errors:")
623			for _, err := range errs {
624				t.Errorf("  %s", err)
625			}
626			t.FailNow()
627		}
628
629		if len(file.Defs) == len(testCase.defs) {
630			for i := range file.Defs {
631				if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) {
632					t.Errorf("test case: %s", testCase.input)
633					t.Errorf("incorrect defintion %d:", i)
634					t.Errorf("  expected: %s", testCase.defs[i])
635					t.Errorf("       got: %s", file.Defs[i])
636				}
637			}
638		} else {
639			t.Errorf("test case: %s", testCase.input)
640			t.Errorf("length mismatch, expected %d definitions, got %d",
641				len(testCase.defs), len(file.Defs))
642		}
643
644		if len(file.Comments) == len(testCase.comments) {
645			for i := range file.Comments {
646				if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) {
647					t.Errorf("test case: %s", testCase.input)
648					t.Errorf("incorrect comment %d:", i)
649					t.Errorf("  expected: %s", testCase.comments[i])
650					t.Errorf("       got: %s", file.Comments[i])
651				}
652			}
653		} else {
654			t.Errorf("test case: %s", testCase.input)
655			t.Errorf("length mismatch, expected %d comments, got %d",
656				len(testCase.comments), len(file.Comments))
657		}
658	}
659}
660
661// TODO: Test error strings
662