1// Copyright 2016 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	"fmt"
19	"strings"
20	"text/scanner"
21)
22
23type Node interface {
24	// Pos returns the position of the first token in the Node
25	Pos() scanner.Position
26	// End returns the position of the character after the last token in the Node
27	End() scanner.Position
28}
29
30// Definition is an Assignment or a Module at the top level of a Blueprints file
31type Definition interface {
32	Node
33	String() string
34	definitionTag()
35}
36
37// An Assignment is a variable assignment at the top level of a Blueprints file, scoped to the
38// file and and subdirs.
39type Assignment struct {
40	Name       string
41	NamePos    scanner.Position
42	Value      Expression
43	OrigValue  Expression
44	EqualsPos  scanner.Position
45	Assigner   string
46	Referenced bool
47}
48
49func (a *Assignment) String() string {
50	return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
51}
52
53func (a *Assignment) Pos() scanner.Position { return a.NamePos }
54func (a *Assignment) End() scanner.Position { return a.Value.End() }
55
56func (a *Assignment) definitionTag() {}
57
58// A Module is a module definition at the top level of a Blueprints file
59type Module struct {
60	Type    string
61	TypePos scanner.Position
62	Map
63}
64
65func (m *Module) Copy() *Module {
66	ret := *m
67	ret.Properties = make([]*Property, len(m.Properties))
68	for i := range m.Properties {
69		ret.Properties[i] = m.Properties[i].Copy()
70	}
71	return &ret
72}
73
74func (m *Module) String() string {
75	propertyStrings := make([]string, len(m.Properties))
76	for i, property := range m.Properties {
77		propertyStrings[i] = property.String()
78	}
79	return fmt.Sprintf("%s@%s-%s{%s}", m.Type,
80		m.LBracePos, m.RBracePos,
81		strings.Join(propertyStrings, ", "))
82}
83
84func (m *Module) definitionTag() {}
85
86func (m *Module) Pos() scanner.Position { return m.TypePos }
87func (m *Module) End() scanner.Position { return m.Map.End() }
88
89// A Property is a name: value pair within a Map, which may be a top level Module.
90type Property struct {
91	Name     string
92	NamePos  scanner.Position
93	ColonPos scanner.Position
94	Value    Expression
95}
96
97func (p *Property) Copy() *Property {
98	ret := *p
99	ret.Value = p.Value.Copy()
100	return &ret
101}
102
103func (p *Property) String() string {
104	return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, p.Value)
105}
106
107func (p *Property) Pos() scanner.Position { return p.NamePos }
108func (p *Property) End() scanner.Position { return p.Value.End() }
109
110// An Expression is a Value in a Property or Assignment.  It can be a literal (String or Bool), a
111// Map, a List, an Operator that combines two expressions of the same type, or a Variable that
112// references and Assignment.
113type Expression interface {
114	Node
115	// Copy returns a copy of the Expression that will not affect the original if mutated
116	Copy() Expression
117	String() string
118	// Type returns the underlying Type enum of the Expression if it were to be evalutated
119	Type() Type
120	// Eval returns an expression that is fully evaluated to a simple type (List, Map, String, or
121	// Bool).  It will return the same object for every call to Eval().
122	Eval() Expression
123}
124
125// ExpressionsAreSame tells whether the two values are the same Expression.
126// This includes the symbolic representation of each Expression but not their positions in the original source tree.
127// This does not apply any simplification to the expressions before comparing them
128// (for example, "!!a" wouldn't be deemed equal to "a")
129func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
130	return hackyExpressionsAreSame(a, b)
131}
132
133// TODO(jeffrygaston) once positions are removed from Expression structs,
134// remove this function and have callers use reflect.DeepEqual(a, b)
135func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
136	if a.Type() != b.Type() {
137		return false, nil
138	}
139	left, err := hackyFingerprint(a)
140	if err != nil {
141		return false, nil
142	}
143	right, err := hackyFingerprint(b)
144	if err != nil {
145		return false, nil
146	}
147	areEqual := string(left) == string(right)
148	return areEqual, nil
149}
150
151func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
152	assignment := &Assignment{"a", noPos, expression, expression, noPos, "=", false}
153	module := &File{}
154	module.Defs = append(module.Defs, assignment)
155	p := newPrinter(module)
156	return p.Print()
157}
158
159type Type int
160
161const (
162	BoolType Type = iota + 1
163	StringType
164	Int64Type
165	ListType
166	MapType
167	NotEvaluatedType
168)
169
170func (t Type) String() string {
171	switch t {
172	case BoolType:
173		return "bool"
174	case StringType:
175		return "string"
176	case Int64Type:
177		return "int64"
178	case ListType:
179		return "list"
180	case MapType:
181		return "map"
182	case NotEvaluatedType:
183		return "notevaluated"
184	default:
185		panic(fmt.Errorf("Unknown type %d", t))
186	}
187}
188
189type Operator struct {
190	Args        [2]Expression
191	Operator    rune
192	OperatorPos scanner.Position
193	Value       Expression
194}
195
196func (x *Operator) Copy() Expression {
197	ret := *x
198	ret.Args[0] = x.Args[0].Copy()
199	ret.Args[1] = x.Args[1].Copy()
200	return &ret
201}
202
203func (x *Operator) Eval() Expression {
204	return x.Value.Eval()
205}
206
207func (x *Operator) Type() Type {
208	return x.Args[0].Type()
209}
210
211func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
212func (x *Operator) End() scanner.Position { return x.Args[1].End() }
213
214func (x *Operator) String() string {
215	return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
216		x.Value, x.OperatorPos)
217}
218
219type Variable struct {
220	Name    string
221	NamePos scanner.Position
222	Value   Expression
223}
224
225func (x *Variable) Pos() scanner.Position { return x.NamePos }
226func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) }
227
228func (x *Variable) Copy() Expression {
229	ret := *x
230	return &ret
231}
232
233func (x *Variable) Eval() Expression {
234	return x.Value.Eval()
235}
236
237func (x *Variable) String() string {
238	return x.Name + " = " + x.Value.String()
239}
240
241func (x *Variable) Type() Type { return x.Value.Type() }
242
243type Map struct {
244	LBracePos  scanner.Position
245	RBracePos  scanner.Position
246	Properties []*Property
247}
248
249func (x *Map) Pos() scanner.Position { return x.LBracePos }
250func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) }
251
252func (x *Map) Copy() Expression {
253	ret := *x
254	ret.Properties = make([]*Property, len(x.Properties))
255	for i := range x.Properties {
256		ret.Properties[i] = x.Properties[i].Copy()
257	}
258	return &ret
259}
260
261func (x *Map) Eval() Expression {
262	return x
263}
264
265func (x *Map) String() string {
266	propertyStrings := make([]string, len(x.Properties))
267	for i, property := range x.Properties {
268		propertyStrings[i] = property.String()
269	}
270	return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos,
271		strings.Join(propertyStrings, ", "))
272}
273
274func (x *Map) Type() Type { return MapType }
275
276// GetProperty looks for a property with the given name.
277// It resembles the bracket operator of a built-in Golang map.
278func (x *Map) GetProperty(name string) (Property *Property, found bool) {
279	prop, found, _ := x.getPropertyImpl(name)
280	return prop, found // we don't currently expose the index to callers
281}
282
283func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) {
284	for i, prop := range x.Properties {
285		if prop.Name == name {
286			return prop, true, i
287		}
288	}
289	return nil, false, -1
290}
291
292// GetProperty removes the property with the given name, if it exists.
293func (x *Map) RemoveProperty(propertyName string) (removed bool) {
294	_, found, index := x.getPropertyImpl(propertyName)
295	if found {
296		x.Properties = append(x.Properties[:index], x.Properties[index+1:]...)
297	}
298	return found
299}
300
301type List struct {
302	LBracePos scanner.Position
303	RBracePos scanner.Position
304	Values    []Expression
305}
306
307func (x *List) Pos() scanner.Position { return x.LBracePos }
308func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) }
309
310func (x *List) Copy() Expression {
311	ret := *x
312	ret.Values = make([]Expression, len(x.Values))
313	for i := range ret.Values {
314		ret.Values[i] = x.Values[i].Copy()
315	}
316	return &ret
317}
318
319func (x *List) Eval() Expression {
320	return x
321}
322
323func (x *List) String() string {
324	valueStrings := make([]string, len(x.Values))
325	for i, value := range x.Values {
326		valueStrings[i] = value.String()
327	}
328	return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos,
329		strings.Join(valueStrings, ", "))
330}
331
332func (x *List) Type() Type { return ListType }
333
334type String struct {
335	LiteralPos scanner.Position
336	Value      string
337}
338
339func (x *String) Pos() scanner.Position { return x.LiteralPos }
340func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) }
341
342func (x *String) Copy() Expression {
343	ret := *x
344	return &ret
345}
346
347func (x *String) Eval() Expression {
348	return x
349}
350
351func (x *String) String() string {
352	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
353}
354
355func (x *String) Type() Type {
356	return StringType
357}
358
359type Int64 struct {
360	LiteralPos scanner.Position
361	Value      int64
362	Token      string
363}
364
365func (x *Int64) Pos() scanner.Position { return x.LiteralPos }
366func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
367
368func (x *Int64) Copy() Expression {
369	ret := *x
370	return &ret
371}
372
373func (x *Int64) Eval() Expression {
374	return x
375}
376
377func (x *Int64) String() string {
378	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
379}
380
381func (x *Int64) Type() Type {
382	return Int64Type
383}
384
385type Bool struct {
386	LiteralPos scanner.Position
387	Value      bool
388	Token      string
389}
390
391func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
392func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
393
394func (x *Bool) Copy() Expression {
395	ret := *x
396	return &ret
397}
398
399func (x *Bool) Eval() Expression {
400	return x
401}
402
403func (x *Bool) String() string {
404	return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos)
405}
406
407func (x *Bool) Type() Type {
408	return BoolType
409}
410
411type CommentGroup struct {
412	Comments []*Comment
413}
414
415func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() }
416func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() }
417
418type Comment struct {
419	Comment []string
420	Slash   scanner.Position
421}
422
423func (c Comment) Pos() scanner.Position {
424	return c.Slash
425}
426
427func (c Comment) End() scanner.Position {
428	pos := c.Slash
429	for _, comment := range c.Comment {
430		pos.Offset += len(comment) + 1
431		pos.Column = len(comment) + 1
432	}
433	pos.Line += len(c.Comment) - 1
434	return pos
435}
436
437func (c Comment) String() string {
438	l := 0
439	for _, comment := range c.Comment {
440		l += len(comment) + 1
441	}
442	buf := make([]byte, 0, l)
443	for _, comment := range c.Comment {
444		buf = append(buf, comment...)
445		buf = append(buf, '\n')
446	}
447
448	return string(buf) + "@" + c.Slash.String()
449}
450
451// Return the text of the comment with // or /* and */ stripped
452func (c Comment) Text() string {
453	l := 0
454	for _, comment := range c.Comment {
455		l += len(comment) + 1
456	}
457	buf := make([]byte, 0, l)
458
459	blockComment := false
460	if strings.HasPrefix(c.Comment[0], "/*") {
461		blockComment = true
462	}
463
464	for i, comment := range c.Comment {
465		if blockComment {
466			if i == 0 {
467				comment = strings.TrimPrefix(comment, "/*")
468			}
469			if i == len(c.Comment)-1 {
470				comment = strings.TrimSuffix(comment, "*/")
471			}
472		} else {
473			comment = strings.TrimPrefix(comment, "//")
474		}
475		buf = append(buf, comment...)
476		buf = append(buf, '\n')
477	}
478
479	return string(buf)
480}
481
482type NotEvaluated struct {
483	Position scanner.Position
484}
485
486func (n NotEvaluated) Copy() Expression {
487	return NotEvaluated{Position: n.Position}
488}
489
490func (n NotEvaluated) String() string {
491	return "Not Evaluated"
492}
493
494func (n NotEvaluated) Type() Type {
495	return NotEvaluatedType
496}
497
498func (n NotEvaluated) Eval() Expression {
499	return NotEvaluated{Position: n.Position}
500}
501
502func (n NotEvaluated) Pos() scanner.Position { return n.Position }
503func (n NotEvaluated) End() scanner.Position { return n.Position }
504
505func endPos(pos scanner.Position, n int) scanner.Position {
506	pos.Offset += n
507	pos.Column += n
508	return pos
509}
510