• 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 []Comment
36}{
37	{`
38		foo {}
39		`,
40		[]Definition{
41			&Module{
42				Type:      Ident{"foo", mkpos(3, 2, 3)},
43				LbracePos: mkpos(7, 2, 7),
44				RbracePos: mkpos(8, 2, 8),
45			},
46		},
47		nil,
48	},
49
50	{`
51		foo {
52			name: "abc",
53		}
54		`,
55		[]Definition{
56			&Module{
57				Type:      Ident{"foo", mkpos(3, 2, 3)},
58				LbracePos: mkpos(7, 2, 7),
59				RbracePos: mkpos(27, 4, 3),
60				Properties: []*Property{
61					{
62						Name: Ident{"name", mkpos(12, 3, 4)},
63						Pos:  mkpos(16, 3, 8),
64						Value: Value{
65							Type:        String,
66							Pos:         mkpos(18, 3, 10),
67							StringValue: "abc",
68						},
69					},
70				},
71			},
72		},
73		nil,
74	},
75
76	{`
77		foo {
78			isGood: true,
79		}
80		`,
81		[]Definition{
82			&Module{
83				Type:      Ident{"foo", mkpos(3, 2, 3)},
84				LbracePos: mkpos(7, 2, 7),
85				RbracePos: mkpos(28, 4, 3),
86				Properties: []*Property{
87					{
88						Name: Ident{"isGood", mkpos(12, 3, 4)},
89						Pos:  mkpos(18, 3, 10),
90						Value: Value{
91							Type:      Bool,
92							Pos:       mkpos(20, 3, 12),
93							BoolValue: true,
94						},
95					},
96				},
97			},
98		},
99		nil,
100	},
101
102	{`
103		foo {
104			stuff: ["asdf", "jkl;", "qwert",
105				"uiop", "bnm,"]
106		}
107		`,
108		[]Definition{
109			&Module{
110				Type:      Ident{"foo", mkpos(3, 2, 3)},
111				LbracePos: mkpos(7, 2, 7),
112				RbracePos: mkpos(67, 5, 3),
113				Properties: []*Property{
114					{
115						Name: Ident{"stuff", mkpos(12, 3, 4)},
116						Pos:  mkpos(17, 3, 9),
117						Value: Value{
118							Type:   List,
119							Pos:    mkpos(19, 3, 11),
120							EndPos: mkpos(63, 4, 19),
121							ListValue: []Value{
122								Value{
123									Type:        String,
124									Pos:         mkpos(20, 3, 12),
125									StringValue: "asdf",
126								},
127								Value{
128									Type:        String,
129									Pos:         mkpos(28, 3, 20),
130									StringValue: "jkl;",
131								},
132								Value{
133									Type:        String,
134									Pos:         mkpos(36, 3, 28),
135									StringValue: "qwert",
136								},
137								Value{
138									Type:        String,
139									Pos:         mkpos(49, 4, 5),
140									StringValue: "uiop",
141								},
142								Value{
143									Type:        String,
144									Pos:         mkpos(57, 4, 13),
145									StringValue: "bnm,",
146								},
147							},
148						},
149					},
150				},
151			},
152		},
153		nil,
154	},
155
156	{`
157		foo {
158			stuff: {
159				isGood: true,
160				name: "bar"
161			}
162		}
163		`,
164		[]Definition{
165			&Module{
166				Type:      Ident{"foo", mkpos(3, 2, 3)},
167				LbracePos: mkpos(7, 2, 7),
168				RbracePos: mkpos(62, 7, 3),
169				Properties: []*Property{
170					{
171						Name: Ident{"stuff", mkpos(12, 3, 4)},
172						Pos:  mkpos(17, 3, 9),
173						Value: Value{
174							Type:   Map,
175							Pos:    mkpos(19, 3, 11),
176							EndPos: mkpos(58, 6, 4),
177							MapValue: []*Property{
178								{
179									Name: Ident{"isGood", mkpos(25, 4, 5)},
180									Pos:  mkpos(31, 4, 11),
181									Value: Value{
182										Type:      Bool,
183										Pos:       mkpos(33, 4, 13),
184										BoolValue: true,
185									},
186								},
187								{
188									Name: Ident{"name", mkpos(43, 5, 5)},
189									Pos:  mkpos(47, 5, 9),
190									Value: Value{
191										Type:        String,
192										Pos:         mkpos(49, 5, 11),
193										StringValue: "bar",
194									},
195								},
196							},
197						},
198					},
199				},
200			},
201		},
202		nil,
203	},
204
205	{`
206		// comment1
207		foo {
208			// comment2
209			isGood: true,  // comment3
210		}
211		`,
212		[]Definition{
213			&Module{
214				Type:      Ident{"foo", mkpos(17, 3, 3)},
215				LbracePos: mkpos(21, 3, 7),
216				RbracePos: mkpos(70, 6, 3),
217				Properties: []*Property{
218					{
219						Name: Ident{"isGood", mkpos(41, 5, 4)},
220						Pos:  mkpos(47, 5, 10),
221						Value: Value{
222							Type:      Bool,
223							Pos:       mkpos(49, 5, 12),
224							BoolValue: true,
225						},
226					},
227				},
228			},
229		},
230		[]Comment{
231			Comment{
232				Comment: []string{"// comment1"},
233				Pos:     mkpos(3, 2, 3),
234			},
235			Comment{
236				Comment: []string{"// comment2"},
237				Pos:     mkpos(26, 4, 4),
238			},
239			Comment{
240				Comment: []string{"// comment3"},
241				Pos:     mkpos(56, 5, 19),
242			},
243		},
244	},
245
246	{`
247		foo {
248			name: "abc",
249		}
250
251		bar {
252			name: "def",
253		}
254		`,
255		[]Definition{
256			&Module{
257				Type:      Ident{"foo", mkpos(3, 2, 3)},
258				LbracePos: mkpos(7, 2, 7),
259				RbracePos: mkpos(27, 4, 3),
260				Properties: []*Property{
261					{
262						Name: Ident{"name", mkpos(12, 3, 4)},
263						Pos:  mkpos(16, 3, 8),
264						Value: Value{
265							Type:        String,
266							Pos:         mkpos(18, 3, 10),
267							StringValue: "abc",
268						},
269					},
270				},
271			},
272			&Module{
273				Type:      Ident{"bar", mkpos(32, 6, 3)},
274				LbracePos: mkpos(36, 6, 7),
275				RbracePos: mkpos(56, 8, 3),
276				Properties: []*Property{
277					{
278						Name: Ident{"name", mkpos(41, 7, 4)},
279						Pos:  mkpos(45, 7, 8),
280						Value: Value{
281							Type:        String,
282							Pos:         mkpos(47, 7, 10),
283							StringValue: "def",
284						},
285					},
286				},
287			},
288		},
289		nil,
290	},
291	{`
292		foo = "stuff"
293		bar = foo
294		baz = foo + bar
295		boo = baz
296		boo += foo
297		`,
298		[]Definition{
299			&Assignment{
300				Name: Ident{"foo", mkpos(3, 2, 3)},
301				Pos:  mkpos(7, 2, 7),
302				Value: Value{
303					Type:        String,
304					Pos:         mkpos(9, 2, 9),
305					StringValue: "stuff",
306				},
307				OrigValue: Value{
308					Type:        String,
309					Pos:         mkpos(9, 2, 9),
310					StringValue: "stuff",
311				},
312				Assigner:   "=",
313				Referenced: true,
314			},
315			&Assignment{
316				Name: Ident{"bar", mkpos(19, 3, 3)},
317				Pos:  mkpos(23, 3, 7),
318				Value: Value{
319					Type:        String,
320					Pos:         mkpos(25, 3, 9),
321					StringValue: "stuff",
322					Variable:    "foo",
323				},
324				OrigValue: Value{
325					Type:        String,
326					Pos:         mkpos(25, 3, 9),
327					StringValue: "stuff",
328					Variable:    "foo",
329				},
330				Assigner:   "=",
331				Referenced: true,
332			},
333			&Assignment{
334				Name: Ident{"baz", mkpos(31, 4, 3)},
335				Pos:  mkpos(35, 4, 7),
336				Value: Value{
337					Type:        String,
338					Pos:         mkpos(37, 4, 9),
339					StringValue: "stuffstuff",
340					Expression: &Expression{
341						Args: [2]Value{
342							{
343								Type:        String,
344								Pos:         mkpos(37, 4, 9),
345								StringValue: "stuff",
346								Variable:    "foo",
347							},
348							{
349								Type:        String,
350								Pos:         mkpos(43, 4, 15),
351								StringValue: "stuff",
352								Variable:    "bar",
353							},
354						},
355						Operator: '+',
356						Pos:      mkpos(41, 4, 13),
357					},
358				},
359				OrigValue: Value{
360					Type:        String,
361					Pos:         mkpos(37, 4, 9),
362					StringValue: "stuffstuff",
363					Expression: &Expression{
364						Args: [2]Value{
365							{
366								Type:        String,
367								Pos:         mkpos(37, 4, 9),
368								StringValue: "stuff",
369								Variable:    "foo",
370							},
371							{
372								Type:        String,
373								Pos:         mkpos(43, 4, 15),
374								StringValue: "stuff",
375								Variable:    "bar",
376							},
377						},
378						Operator: '+',
379						Pos:      mkpos(41, 4, 13),
380					},
381				},
382				Assigner:   "=",
383				Referenced: true,
384			},
385			&Assignment{
386				Name: Ident{"boo", mkpos(49, 5, 3)},
387				Pos:  mkpos(53, 5, 7),
388				Value: Value{
389					Type:        String,
390					Pos:         mkpos(55, 5, 9),
391					StringValue: "stuffstuffstuff",
392					Expression: &Expression{
393						Args: [2]Value{
394							{
395								Type:        String,
396								Pos:         mkpos(55, 5, 9),
397								StringValue: "stuffstuff",
398								Variable:    "baz",
399								Expression: &Expression{
400									Args: [2]Value{
401										{
402											Type:        String,
403											Pos:         mkpos(37, 4, 9),
404											StringValue: "stuff",
405											Variable:    "foo",
406										},
407										{
408											Type:        String,
409											Pos:         mkpos(43, 4, 15),
410											StringValue: "stuff",
411											Variable:    "bar",
412										},
413									},
414									Operator: '+',
415									Pos:      mkpos(41, 4, 13),
416								},
417							},
418							{
419								Variable:    "foo",
420								Type:        String,
421								Pos:         mkpos(68, 6, 10),
422								StringValue: "stuff",
423							},
424						},
425						Pos:      mkpos(66, 6, 8),
426						Operator: '+',
427					},
428				},
429				OrigValue: Value{
430					Type:        String,
431					Pos:         mkpos(55, 5, 9),
432					StringValue: "stuffstuff",
433					Variable:    "baz",
434					Expression: &Expression{
435						Args: [2]Value{
436							{
437								Type:        String,
438								Pos:         mkpos(37, 4, 9),
439								StringValue: "stuff",
440								Variable:    "foo",
441							},
442							{
443								Type:        String,
444								Pos:         mkpos(43, 4, 15),
445								StringValue: "stuff",
446								Variable:    "bar",
447							},
448						},
449						Operator: '+',
450						Pos:      mkpos(41, 4, 13),
451					},
452				},
453				Assigner: "=",
454			},
455			&Assignment{
456				Name: Ident{"boo", mkpos(61, 6, 3)},
457				Pos:  mkpos(66, 6, 8),
458				Value: Value{
459					Type:        String,
460					Pos:         mkpos(68, 6, 10),
461					StringValue: "stuff",
462					Variable:    "foo",
463				},
464				OrigValue: Value{
465					Type:        String,
466					Pos:         mkpos(68, 6, 10),
467					StringValue: "stuff",
468					Variable:    "foo",
469				},
470				Assigner: "+=",
471			},
472		},
473		nil,
474	},
475}
476
477func TestParseValidInput(t *testing.T) {
478	for _, testCase := range validParseTestCases {
479		r := bytes.NewBufferString(testCase.input)
480		file, errs := ParseAndEval("", r, NewScope(nil))
481		if len(errs) != 0 {
482			t.Errorf("test case: %s", testCase.input)
483			t.Errorf("unexpected errors:")
484			for _, err := range errs {
485				t.Errorf("  %s", err)
486			}
487			t.FailNow()
488		}
489
490		if len(file.Defs) == len(testCase.defs) {
491			for i := range file.Defs {
492				if !reflect.DeepEqual(file.Defs[i], testCase.defs[i]) {
493					t.Errorf("test case: %s", testCase.input)
494					t.Errorf("incorrect defintion %d:", i)
495					t.Errorf("  expected: %s", testCase.defs[i])
496					t.Errorf("       got: %s", file.Defs[i])
497				}
498			}
499		} else {
500			t.Errorf("test case: %s", testCase.input)
501			t.Errorf("length mismatch, expected %d definitions, got %d",
502				len(testCase.defs), len(file.Defs))
503		}
504
505		if len(file.Comments) == len(testCase.comments) {
506			for i := range file.Comments {
507				if !reflect.DeepEqual(file.Comments, testCase.comments) {
508					t.Errorf("test case: %s", testCase.input)
509					t.Errorf("incorrect comment %d:", i)
510					t.Errorf("  expected: %s", testCase.comments[i])
511					t.Errorf("       got: %s", file.Comments[i])
512				}
513			}
514		} else {
515			t.Errorf("test case: %s", testCase.input)
516			t.Errorf("length mismatch, expected %d comments, got %d",
517				len(testCase.comments), len(file.Comments))
518		}
519	}
520}
521
522// TODO: Test error strings
523