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