blob: fb7e51678fb97a9f29ac29bb19847b163ff5cf5b [file] [log] [blame]
Colin Crosse32cc802016-06-07 12:28:16 -07001// 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
Colin Cross1ead6452016-06-09 17:40:13 -070023type Node interface {
Colin Crossfdeaf882018-03-21 17:00:39 -070024 // Pos returns the position of the first token in the Node
Colin Cross1ead6452016-06-09 17:40:13 -070025 Pos() scanner.Position
Colin Crossfdeaf882018-03-21 17:00:39 -070026 // End returns the position of the character after the last token in the Node
Colin Cross1ead6452016-06-09 17:40:13 -070027 End() scanner.Position
28}
29
Colin Crosse32cc802016-06-07 12:28:16 -070030// Definition is an Assignment or a Module at the top level of a Blueprints file
31type Definition interface {
Colin Cross1ead6452016-06-09 17:40:13 -070032 Node
Colin Crosse32cc802016-06-07 12:28:16 -070033 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 {
Colin Crossc32c4792016-06-09 15:52:30 -070040 Name string
41 NamePos scanner.Position
Colin Crosse32cc802016-06-07 12:28:16 -070042 Value Expression
43 OrigValue Expression
Colin Crossb3d0b8d2016-06-09 17:03:57 -070044 EqualsPos scanner.Position
Colin Crosse32cc802016-06-07 12:28:16 -070045 Assigner string
46 Referenced bool
47}
48
49func (a *Assignment) String() string {
Colin Crossb3d0b8d2016-06-09 17:03:57 -070050 return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
Colin Crosse32cc802016-06-07 12:28:16 -070051}
52
Colin Cross1ead6452016-06-09 17:40:13 -070053func (a *Assignment) Pos() scanner.Position { return a.NamePos }
54func (a *Assignment) End() scanner.Position { return a.Value.End() }
55
Colin Crosse32cc802016-06-07 12:28:16 -070056func (a *Assignment) definitionTag() {}
57
58// A Module is a module definition at the top level of a Blueprints file
59type Module struct {
Colin Crossc32c4792016-06-09 15:52:30 -070060 Type string
61 TypePos scanner.Position
Colin Crosse32cc802016-06-07 12:28:16 -070062 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
Colin Crossc32c4792016-06-09 15:52:30 -070086func (m *Module) Pos() scanner.Position { return m.TypePos }
87func (m *Module) End() scanner.Position { return m.Map.End() }
88
Colin Crosse32cc802016-06-07 12:28:16 -070089// A Property is a name: value pair within a Map, which may be a top level Module.
90type Property struct {
Colin Crossb3d0b8d2016-06-09 17:03:57 -070091 Name string
92 NamePos scanner.Position
93 ColonPos scanner.Position
94 Value Expression
Colin Crosse32cc802016-06-07 12:28:16 -070095}
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 {
Colin Crossb3d0b8d2016-06-09 17:03:57 -0700104 return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, p.Value)
Colin Crosse32cc802016-06-07 12:28:16 -0700105}
106
Colin Cross1ead6452016-06-09 17:40:13 -0700107func (p *Property) Pos() scanner.Position { return p.NamePos }
108func (p *Property) End() scanner.Position { return p.Value.End() }
109
Colin Crosse32cc802016-06-07 12:28:16 -0700110// 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 {
Colin Cross1ead6452016-06-09 17:40:13 -0700114 Node
Colin Crosse32cc802016-06-07 12:28:16 -0700115 // 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
Colin Crosse32cc802016-06-07 12:28:16 -0700120 // 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
Jeff Gaston5d4b9d82017-05-16 17:39:37 -0700125// 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
Sasha Smundak29fdcad2020-02-11 22:39:47 -0800133// TODO(jeffrygaston) once positions are removed from Expression structs,
Jeff Gaston5d4b9d82017-05-16 17:39:37 -0700134// 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
Colin Crosse32cc802016-06-07 12:28:16 -0700159type Type int
160
161const (
162 BoolType Type = iota + 1
163 StringType
Nan Zhangf5865442017-11-01 14:03:28 -0700164 Int64Type
Colin Crosse32cc802016-06-07 12:28:16 -0700165 ListType
166 MapType
Sasha Smundak77418b72020-01-21 13:31:06 -0800167 NotEvaluatedType
Colin Crosse32cc802016-06-07 12:28:16 -0700168)
169
170func (t Type) String() string {
171 switch t {
172 case BoolType:
173 return "bool"
174 case StringType:
175 return "string"
Nan Zhangf5865442017-11-01 14:03:28 -0700176 case Int64Type:
177 return "int64"
Colin Crosse32cc802016-06-07 12:28:16 -0700178 case ListType:
179 return "list"
180 case MapType:
181 return "map"
Sasha Smundak77418b72020-01-21 13:31:06 -0800182 case NotEvaluatedType:
183 return "notevaluated"
Colin Crosse32cc802016-06-07 12:28:16 -0700184 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
Colin Crosse32cc802016-06-07 12:28:16 -0700221 NamePos scanner.Position
Colin Cross1ead6452016-06-09 17:40:13 -0700222 Value Expression
Colin Crosse32cc802016-06-07 12:28:16 -0700223}
224
225func (x *Variable) Pos() scanner.Position { return x.NamePos }
Colin Crossfdeaf882018-03-21 17:00:39 -0700226func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) }
Colin Crosse32cc802016-06-07 12:28:16 -0700227
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 }
Colin Crossfdeaf882018-03-21 17:00:39 -0700250func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) }
Colin Crosse32cc802016-06-07 12:28:16 -0700251
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
Jeff Gaston5d4b9d82017-05-16 17:39:37 -0700276// 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
Colin Crosse32cc802016-06-07 12:28:16 -0700301type List struct {
302 LBracePos scanner.Position
303 RBracePos scanner.Position
304 Values []Expression
305}
306
307func (x *List) Pos() scanner.Position { return x.LBracePos }
Colin Crossfdeaf882018-03-21 17:00:39 -0700308func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) }
Colin Crosse32cc802016-06-07 12:28:16 -0700309
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 }
Colin Crossfdeaf882018-03-21 17:00:39 -0700340func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) }
Colin Crosse32cc802016-06-07 12:28:16 -0700341
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
Nan Zhangf5865442017-11-01 14:03:28 -0700359type Int64 struct {
360 LiteralPos scanner.Position
361 Value int64
Colin Crossfdeaf882018-03-21 17:00:39 -0700362 Token string
Nan Zhangf5865442017-11-01 14:03:28 -0700363}
364
365func (x *Int64) Pos() scanner.Position { return x.LiteralPos }
Colin Crossfdeaf882018-03-21 17:00:39 -0700366func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
Nan Zhangf5865442017-11-01 14:03:28 -0700367
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
Colin Crosse32cc802016-06-07 12:28:16 -0700385type Bool struct {
386 LiteralPos scanner.Position
387 Value bool
Colin Crossfdeaf882018-03-21 17:00:39 -0700388 Token string
Colin Crosse32cc802016-06-07 12:28:16 -0700389}
390
391func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
Colin Crossfdeaf882018-03-21 17:00:39 -0700392func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
Colin Crosse32cc802016-06-07 12:28:16 -0700393
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
Colin Cross1e737942016-06-10 17:27:12 -0700411type 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
Colin Crosse32cc802016-06-07 12:28:16 -0700418type 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 {
Colin Crossfdeaf882018-03-21 17:00:39 -0700430 pos.Offset += len(comment) + 1
431 pos.Column = len(comment) + 1
Colin Crosse32cc802016-06-07 12:28:16 -0700432 }
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}
Colin Crossfdeaf882018-03-21 17:00:39 -0700481
Sasha Smundak77418b72020-01-21 13:31:06 -0800482type 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
Colin Crossfdeaf882018-03-21 17:00:39 -0700505func endPos(pos scanner.Position, n int) scanner.Position {
506 pos.Offset += n
507 pos.Column += n
508 return pos
509}