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