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