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