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 stucts, 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) 168 169func (t Type) String() string { 170 switch t { 171 case BoolType: 172 return "bool" 173 case StringType: 174 return "string" 175 case Int64Type: 176 return "int64" 177 case ListType: 178 return "list" 179 case MapType: 180 return "map" 181 default: 182 panic(fmt.Errorf("Unknown type %d", t)) 183 } 184} 185 186type Operator struct { 187 Args [2]Expression 188 Operator rune 189 OperatorPos scanner.Position 190 Value Expression 191} 192 193func (x *Operator) Copy() Expression { 194 ret := *x 195 ret.Args[0] = x.Args[0].Copy() 196 ret.Args[1] = x.Args[1].Copy() 197 return &ret 198} 199 200func (x *Operator) Eval() Expression { 201 return x.Value.Eval() 202} 203 204func (x *Operator) Type() Type { 205 return x.Args[0].Type() 206} 207 208func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() } 209func (x *Operator) End() scanner.Position { return x.Args[1].End() } 210 211func (x *Operator) String() string { 212 return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(), 213 x.Value, x.OperatorPos) 214} 215 216type Variable struct { 217 Name string 218 NamePos scanner.Position 219 Value Expression 220} 221 222func (x *Variable) Pos() scanner.Position { return x.NamePos } 223func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) } 224 225func (x *Variable) Copy() Expression { 226 ret := *x 227 return &ret 228} 229 230func (x *Variable) Eval() Expression { 231 return x.Value.Eval() 232} 233 234func (x *Variable) String() string { 235 return x.Name + " = " + x.Value.String() 236} 237 238func (x *Variable) Type() Type { return x.Value.Type() } 239 240type Map struct { 241 LBracePos scanner.Position 242 RBracePos scanner.Position 243 Properties []*Property 244} 245 246func (x *Map) Pos() scanner.Position { return x.LBracePos } 247func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) } 248 249func (x *Map) Copy() Expression { 250 ret := *x 251 ret.Properties = make([]*Property, len(x.Properties)) 252 for i := range x.Properties { 253 ret.Properties[i] = x.Properties[i].Copy() 254 } 255 return &ret 256} 257 258func (x *Map) Eval() Expression { 259 return x 260} 261 262func (x *Map) String() string { 263 propertyStrings := make([]string, len(x.Properties)) 264 for i, property := range x.Properties { 265 propertyStrings[i] = property.String() 266 } 267 return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos, 268 strings.Join(propertyStrings, ", ")) 269} 270 271func (x *Map) Type() Type { return MapType } 272 273// GetProperty looks for a property with the given name. 274// It resembles the bracket operator of a built-in Golang map. 275func (x *Map) GetProperty(name string) (Property *Property, found bool) { 276 prop, found, _ := x.getPropertyImpl(name) 277 return prop, found // we don't currently expose the index to callers 278} 279 280func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) { 281 for i, prop := range x.Properties { 282 if prop.Name == name { 283 return prop, true, i 284 } 285 } 286 return nil, false, -1 287} 288 289// GetProperty removes the property with the given name, if it exists. 290func (x *Map) RemoveProperty(propertyName string) (removed bool) { 291 _, found, index := x.getPropertyImpl(propertyName) 292 if found { 293 x.Properties = append(x.Properties[:index], x.Properties[index+1:]...) 294 } 295 return found 296} 297 298type List struct { 299 LBracePos scanner.Position 300 RBracePos scanner.Position 301 Values []Expression 302} 303 304func (x *List) Pos() scanner.Position { return x.LBracePos } 305func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) } 306 307func (x *List) Copy() Expression { 308 ret := *x 309 ret.Values = make([]Expression, len(x.Values)) 310 for i := range ret.Values { 311 ret.Values[i] = x.Values[i].Copy() 312 } 313 return &ret 314} 315 316func (x *List) Eval() Expression { 317 return x 318} 319 320func (x *List) String() string { 321 valueStrings := make([]string, len(x.Values)) 322 for i, value := range x.Values { 323 valueStrings[i] = value.String() 324 } 325 return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos, 326 strings.Join(valueStrings, ", ")) 327} 328 329func (x *List) Type() Type { return ListType } 330 331type String struct { 332 LiteralPos scanner.Position 333 Value string 334} 335 336func (x *String) Pos() scanner.Position { return x.LiteralPos } 337func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) } 338 339func (x *String) Copy() Expression { 340 ret := *x 341 return &ret 342} 343 344func (x *String) Eval() Expression { 345 return x 346} 347 348func (x *String) String() string { 349 return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos) 350} 351 352func (x *String) Type() Type { 353 return StringType 354} 355 356type Int64 struct { 357 LiteralPos scanner.Position 358 Value int64 359 Token string 360} 361 362func (x *Int64) Pos() scanner.Position { return x.LiteralPos } 363func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) } 364 365func (x *Int64) Copy() Expression { 366 ret := *x 367 return &ret 368} 369 370func (x *Int64) Eval() Expression { 371 return x 372} 373 374func (x *Int64) String() string { 375 return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos) 376} 377 378func (x *Int64) Type() Type { 379 return Int64Type 380} 381 382type Bool struct { 383 LiteralPos scanner.Position 384 Value bool 385 Token string 386} 387 388func (x *Bool) Pos() scanner.Position { return x.LiteralPos } 389func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) } 390 391func (x *Bool) Copy() Expression { 392 ret := *x 393 return &ret 394} 395 396func (x *Bool) Eval() Expression { 397 return x 398} 399 400func (x *Bool) String() string { 401 return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos) 402} 403 404func (x *Bool) Type() Type { 405 return BoolType 406} 407 408type CommentGroup struct { 409 Comments []*Comment 410} 411 412func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() } 413func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() } 414 415type Comment struct { 416 Comment []string 417 Slash scanner.Position 418} 419 420func (c Comment) Pos() scanner.Position { 421 return c.Slash 422} 423 424func (c Comment) End() scanner.Position { 425 pos := c.Slash 426 for _, comment := range c.Comment { 427 pos.Offset += len(comment) + 1 428 pos.Column = len(comment) + 1 429 } 430 pos.Line += len(c.Comment) - 1 431 return pos 432} 433 434func (c Comment) String() string { 435 l := 0 436 for _, comment := range c.Comment { 437 l += len(comment) + 1 438 } 439 buf := make([]byte, 0, l) 440 for _, comment := range c.Comment { 441 buf = append(buf, comment...) 442 buf = append(buf, '\n') 443 } 444 445 return string(buf) + "@" + c.Slash.String() 446} 447 448// Return the text of the comment with // or /* and */ stripped 449func (c Comment) Text() string { 450 l := 0 451 for _, comment := range c.Comment { 452 l += len(comment) + 1 453 } 454 buf := make([]byte, 0, l) 455 456 blockComment := false 457 if strings.HasPrefix(c.Comment[0], "/*") { 458 blockComment = true 459 } 460 461 for i, comment := range c.Comment { 462 if blockComment { 463 if i == 0 { 464 comment = strings.TrimPrefix(comment, "/*") 465 } 466 if i == len(c.Comment)-1 { 467 comment = strings.TrimSuffix(comment, "*/") 468 } 469 } else { 470 comment = strings.TrimPrefix(comment, "//") 471 } 472 buf = append(buf, comment...) 473 buf = append(buf, '\n') 474 } 475 476 return string(buf) 477} 478 479func endPos(pos scanner.Position, n int) scanner.Position { 480 pos.Offset += n 481 pos.Column += n 482 return pos 483} 484