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