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