• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Expression
25	Pos() scanner.Position
26	// End returns the position of the beginning of the last token in the Expression
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 stucts,
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	ListType
165	MapType
166)
167
168func (t Type) String() string {
169	switch t {
170	case BoolType:
171		return "bool"
172	case StringType:
173		return "string"
174	case ListType:
175		return "list"
176	case MapType:
177		return "map"
178	default:
179		panic(fmt.Errorf("Unknown type %d", t))
180	}
181}
182
183type Operator struct {
184	Args        [2]Expression
185	Operator    rune
186	OperatorPos scanner.Position
187	Value       Expression
188}
189
190func (x *Operator) Copy() Expression {
191	ret := *x
192	ret.Args[0] = x.Args[0].Copy()
193	ret.Args[1] = x.Args[1].Copy()
194	return &ret
195}
196
197func (x *Operator) Eval() Expression {
198	return x.Value.Eval()
199}
200
201func (x *Operator) Type() Type {
202	return x.Args[0].Type()
203}
204
205func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
206func (x *Operator) End() scanner.Position { return x.Args[1].End() }
207
208func (x *Operator) String() string {
209	return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
210		x.Value, x.OperatorPos)
211}
212
213type Variable struct {
214	Name    string
215	NamePos scanner.Position
216	Value   Expression
217}
218
219func (x *Variable) Pos() scanner.Position { return x.NamePos }
220func (x *Variable) End() scanner.Position { return x.NamePos }
221
222func (x *Variable) Copy() Expression {
223	ret := *x
224	return &ret
225}
226
227func (x *Variable) Eval() Expression {
228	return x.Value.Eval()
229}
230
231func (x *Variable) String() string {
232	return x.Name + " = " + x.Value.String()
233}
234
235func (x *Variable) Type() Type { return x.Value.Type() }
236
237type Map struct {
238	LBracePos  scanner.Position
239	RBracePos  scanner.Position
240	Properties []*Property
241}
242
243func (x *Map) Pos() scanner.Position { return x.LBracePos }
244func (x *Map) End() scanner.Position { return x.RBracePos }
245
246func (x *Map) Copy() Expression {
247	ret := *x
248	ret.Properties = make([]*Property, len(x.Properties))
249	for i := range x.Properties {
250		ret.Properties[i] = x.Properties[i].Copy()
251	}
252	return &ret
253}
254
255func (x *Map) Eval() Expression {
256	return x
257}
258
259func (x *Map) String() string {
260	propertyStrings := make([]string, len(x.Properties))
261	for i, property := range x.Properties {
262		propertyStrings[i] = property.String()
263	}
264	return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos,
265		strings.Join(propertyStrings, ", "))
266}
267
268func (x *Map) Type() Type { return MapType }
269
270// GetProperty looks for a property with the given name.
271// It resembles the bracket operator of a built-in Golang map.
272func (x *Map) GetProperty(name string) (Property *Property, found bool) {
273	prop, found, _ := x.getPropertyImpl(name)
274	return prop, found // we don't currently expose the index to callers
275}
276
277func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) {
278	for i, prop := range x.Properties {
279		if prop.Name == name {
280			return prop, true, i
281		}
282	}
283	return nil, false, -1
284}
285
286// GetProperty removes the property with the given name, if it exists.
287func (x *Map) RemoveProperty(propertyName string) (removed bool) {
288	_, found, index := x.getPropertyImpl(propertyName)
289	if found {
290		x.Properties = append(x.Properties[:index], x.Properties[index+1:]...)
291	}
292	return found
293}
294
295type List struct {
296	LBracePos scanner.Position
297	RBracePos scanner.Position
298	Values    []Expression
299}
300
301func (x *List) Pos() scanner.Position { return x.LBracePos }
302func (x *List) End() scanner.Position { return x.RBracePos }
303
304func (x *List) Copy() Expression {
305	ret := *x
306	ret.Values = make([]Expression, len(x.Values))
307	for i := range ret.Values {
308		ret.Values[i] = x.Values[i].Copy()
309	}
310	return &ret
311}
312
313func (x *List) Eval() Expression {
314	return x
315}
316
317func (x *List) String() string {
318	valueStrings := make([]string, len(x.Values))
319	for i, value := range x.Values {
320		valueStrings[i] = value.String()
321	}
322	return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos,
323		strings.Join(valueStrings, ", "))
324}
325
326func (x *List) Type() Type { return ListType }
327
328type String struct {
329	LiteralPos scanner.Position
330	Value      string
331}
332
333func (x *String) Pos() scanner.Position { return x.LiteralPos }
334func (x *String) End() scanner.Position { return x.LiteralPos }
335
336func (x *String) Copy() Expression {
337	ret := *x
338	return &ret
339}
340
341func (x *String) Eval() Expression {
342	return x
343}
344
345func (x *String) String() string {
346	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
347}
348
349func (x *String) Type() Type {
350	return StringType
351}
352
353type Bool struct {
354	LiteralPos scanner.Position
355	Value      bool
356}
357
358func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
359func (x *Bool) End() scanner.Position { return x.LiteralPos }
360
361func (x *Bool) Copy() Expression {
362	ret := *x
363	return &ret
364}
365
366func (x *Bool) Eval() Expression {
367	return x
368}
369
370func (x *Bool) String() string {
371	return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos)
372}
373
374func (x *Bool) Type() Type {
375	return BoolType
376}
377
378type CommentGroup struct {
379	Comments []*Comment
380}
381
382func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() }
383func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() }
384
385type Comment struct {
386	Comment []string
387	Slash   scanner.Position
388}
389
390func (c Comment) Pos() scanner.Position {
391	return c.Slash
392}
393
394func (c Comment) End() scanner.Position {
395	pos := c.Slash
396	for _, comment := range c.Comment {
397		pos.Offset += len(comment)
398	}
399	pos.Line += len(c.Comment) - 1
400	return pos
401}
402
403func (c Comment) String() string {
404	l := 0
405	for _, comment := range c.Comment {
406		l += len(comment) + 1
407	}
408	buf := make([]byte, 0, l)
409	for _, comment := range c.Comment {
410		buf = append(buf, comment...)
411		buf = append(buf, '\n')
412	}
413
414	return string(buf) + "@" + c.Slash.String()
415}
416
417// Return the text of the comment with // or /* and */ stripped
418func (c Comment) Text() string {
419	l := 0
420	for _, comment := range c.Comment {
421		l += len(comment) + 1
422	}
423	buf := make([]byte, 0, l)
424
425	blockComment := false
426	if strings.HasPrefix(c.Comment[0], "/*") {
427		blockComment = true
428	}
429
430	for i, comment := range c.Comment {
431		if blockComment {
432			if i == 0 {
433				comment = strings.TrimPrefix(comment, "/*")
434			}
435			if i == len(c.Comment)-1 {
436				comment = strings.TrimSuffix(comment, "*/")
437			}
438		} else {
439			comment = strings.TrimPrefix(comment, "//")
440		}
441		buf = append(buf, comment...)
442		buf = append(buf, '\n')
443	}
444
445	return string(buf)
446}
447