github.com/openconfig/goyang@v1.4.5/pkg/yang/types.go (about) 1 // Copyright 2015 Google Inc. 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 15 package yang 16 17 // This file implements the functions relating to types and typedefs. 18 19 import ( 20 "errors" 21 "fmt" 22 "regexp/syntax" 23 "sync" 24 ) 25 26 // A typeDictionary is a dictionary of all Typedefs defined in all Typedefers. 27 // A map of Nodes is used rather than a map of Typedefers to simplify usage 28 // when traversing up a Node tree. 29 type typeDictionary struct { 30 mu sync.Mutex 31 dict map[Node]map[string]*Typedef 32 // identities contains a dictionary of resolved identities. 33 identities identityDictionary 34 } 35 36 func newTypeDictionary() *typeDictionary { 37 return &typeDictionary{ 38 dict: map[Node]map[string]*Typedef{}, 39 identities: identityDictionary{dict: map[string]resolvedIdentity{}}, 40 } 41 } 42 43 // add adds an entry to the typeDictionary d. 44 func (d *typeDictionary) add(n Node, name string, td *Typedef) { 45 defer d.mu.Unlock() 46 d.mu.Lock() 47 if d.dict[n] == nil { 48 d.dict[n] = map[string]*Typedef{} 49 } 50 d.dict[n][name] = td 51 } 52 53 // find returns the Typedef name define in node n, or nil. 54 func (d *typeDictionary) find(n Node, name string) *Typedef { 55 defer d.mu.Unlock() 56 d.mu.Lock() 57 if d.dict[n] == nil { 58 return nil 59 } 60 return d.dict[n][name] 61 } 62 63 // findExternal finds the externally-defined typedef name in a module imported 64 // by n's root with the specified prefix. 65 func (d *typeDictionary) findExternal(n Node, prefix, name string) (*Typedef, error) { 66 root := FindModuleByPrefix(n, prefix) 67 if root == nil { 68 return nil, fmt.Errorf("%s: unknown prefix: %s for type %s", Source(n), prefix, name) 69 } 70 if td := d.find(root, name); td != nil { 71 return td, nil 72 } 73 if prefix != "" { 74 name = prefix + ":" + name 75 } 76 return nil, fmt.Errorf("%s: unknown type %s", Source(n), name) 77 } 78 79 // typedefs returns a slice of all typedefs in d. 80 func (d *typeDictionary) typedefs() []*Typedef { 81 var tds []*Typedef 82 defer d.mu.Unlock() 83 d.mu.Lock() 84 for _, dict := range d.dict { 85 for _, td := range dict { 86 tds = append(tds, td) 87 } 88 } 89 return tds 90 } 91 92 // addTypedefs is called from BuildAST after each Typedefer is defined. There 93 // are no error conditions in this process as it is simply used to build up the 94 // typedef dictionary. 95 func (d *typeDictionary) addTypedefs(t Typedefer) { 96 for _, td := range t.Typedefs() { 97 d.add(t, td.Name, td) 98 } 99 } 100 101 // resolveTypedefs is called after all of modules and submodules have been read, 102 // as well as their imports and includes. It resolves all typedefs found in all 103 // modules and submodules read in. 104 func (d *typeDictionary) resolveTypedefs() []error { 105 var errs []error 106 107 // When resolve typedefs, we may need to look up other typedefs. 108 // We gather all typedefs into a slice so we don't deadlock on 109 // typeDict. 110 for _, td := range d.typedefs() { 111 errs = append(errs, td.resolve(d)...) 112 } 113 return errs 114 } 115 116 // resolve creates a YangType for t, if not already done. Resolving t 117 // requires resolving the Type that t is based on. 118 func (t *Typedef) resolve(d *typeDictionary) []error { 119 // If we have no parent we are a base type and 120 // are already resolved. 121 if t.Parent == nil || t.YangType != nil { 122 return nil 123 } 124 125 if errs := t.Type.resolve(d); len(errs) != 0 { 126 return errs 127 } 128 129 // Make a copy of the YangType we are based on and then 130 // update it with local information. 131 y := *t.Type.YangType 132 y.Name = t.Name 133 y.Base = t.Type 134 135 if t.Units != nil { 136 y.Units = t.Units.Name 137 } 138 if t.Default != nil { 139 y.HasDefault = true 140 y.Default = t.Default.Name 141 } 142 143 if t.Type.IdentityBase != nil { 144 // We need to copy over the IdentityBase statement if the type has one 145 if idBase, err := RootNode(t).findIdentityBase(t.Type.IdentityBase.Name); err == nil { 146 y.IdentityBase = idBase.Identity 147 } else { 148 return []error{fmt.Errorf("could not resolve identity base for typedef: %s", t.Type.IdentityBase.Name)} 149 } 150 } 151 152 // If we changed something, we are the new root. 153 if y.Root == t.Type.YangType || !y.Equal(y.Root) { 154 y.Root = &y 155 } 156 t.YangType = &y 157 return nil 158 } 159 160 // resolve resolves Type t, as well as the underlying typedef for t. If t 161 // cannot be resolved then one or more errors are returned. 162 func (t *Type) resolve(d *typeDictionary) (errs []error) { 163 if t.YangType != nil { 164 return nil 165 } 166 167 // If t.Name is a base type then td will not be nil, otherwise 168 // td will be nil and of type *Typedef. 169 td := BaseTypedefs[t.Name] 170 171 prefix, name := getPrefix(t.Name) 172 root := RootNode(t) 173 rootPrefix := root.GetPrefix() 174 175 source := "unknown" 176 check: 177 switch { 178 case td != nil: 179 source = "builtin" 180 // This was a base type 181 case prefix == "" || rootPrefix == prefix: 182 source = "local" 183 // If we have no prefix, or the prefix is what we call our own 184 // root, then we look in our ancestors for a typedef of name. 185 for n := Node(t); n != nil; n = n.ParentNode() { 186 if td = d.find(n, name); td != nil { 187 break check 188 } 189 } 190 // We need to check our sub-modules as well 191 for _, in := range root.Include { 192 if td = d.find(in.Module, name); td != nil { 193 break check 194 } 195 } 196 var pname string 197 switch { 198 case prefix == "", prefix == root.Prefix.Name: 199 pname = root.Prefix.Name + ":" + t.Name 200 default: 201 pname = fmt.Sprintf("%s[%s]:%s", prefix, root.Prefix.Name, t.Name) 202 } 203 204 return []error{fmt.Errorf("%s: unknown type: %s", Source(t), pname)} 205 206 default: 207 source = "imported" 208 // prefix is not local to our module, so we have to go find 209 // what module it is part of and if it is defined at the top 210 // level of that module. 211 var err error 212 td, err = d.findExternal(t, prefix, name) 213 if err != nil { 214 return []error{err} 215 } 216 } 217 if errs := td.resolve(d); len(errs) > 0 { 218 return errs 219 } 220 221 // Make a copy of the typedef we are based on so we can 222 // augment it. 223 if td.YangType == nil { 224 return []error{fmt.Errorf("%s: no YangType defined for %s %s", Source(td), source, td.Name)} 225 } 226 y := *td.YangType 227 228 y.Base = td.Type 229 t.YangType = &y 230 231 if v := t.RequireInstance; v != nil { 232 b, err := v.asBool() 233 if err != nil { 234 errs = append(errs, err) 235 } 236 y.OptionalInstance = !b 237 } 238 if v := t.Path; v != nil { 239 y.Path = v.asString() 240 } 241 isDecimal64 := y.Kind == Ydecimal64 && (t.Name == "decimal64" || y.FractionDigits != 0) 242 switch { 243 case isDecimal64 && y.FractionDigits != 0: 244 if t.FractionDigits != nil { 245 return append(errs, fmt.Errorf("%s: overriding of fraction-digits not allowed", Source(t))) 246 } 247 // FractionDigits already set via type inheritance. 248 case isDecimal64: 249 // If we are directly of type decimal64 then we must specify 250 // fraction-digits in the range from 1-18. 251 i, err := t.FractionDigits.asRangeInt(1, 18) 252 if err != nil { 253 errs = append(errs, fmt.Errorf("%s: %v", Source(t), err)) 254 } 255 y.FractionDigits = int(i) 256 // We only know to how to populate Range after knowing the 257 // fractional digit value. 258 y.Range = YangRange{{ 259 Number{Value: AbsMinInt64, Negative: true, FractionDigits: uint8(i)}, 260 Number{Value: MaxInt64, FractionDigits: uint8(i)}, 261 }} 262 case t.FractionDigits != nil: 263 errs = append(errs, fmt.Errorf("%s: fraction-digits only allowed for decimal64 values", Source(t))) 264 case y.Kind == Yidentityref: 265 if source != "builtin" { 266 // This is a typedef that refers to an identityref, so we want to simply 267 // maintain the base that the typedef resolution provided 268 break 269 } 270 271 if t.IdentityBase == nil { 272 errs = append(errs, fmt.Errorf("%s: an identityref must specify a base", Source(t))) 273 break 274 } 275 276 root := RootNode(t.Parent) 277 resolvedBase, baseErr := root.findIdentityBase(t.IdentityBase.Name) 278 if baseErr != nil { 279 errs = append(errs, baseErr...) 280 break 281 } 282 283 if resolvedBase.Identity == nil { 284 errs = append(errs, fmt.Errorf("%s: identity has a null base", t.IdentityBase.Name)) 285 break 286 } 287 y.IdentityBase = resolvedBase.Identity 288 } 289 290 if t.Range != nil { 291 yr, err := y.Range.parseChildRanges(t.Range.Name, isDecimal64, uint8(y.FractionDigits)) 292 switch { 293 case err != nil: 294 errs = append(errs, fmt.Errorf("%s: bad range: %v", Source(t.Range), err)) 295 case yr.Equal(y.Range): 296 default: 297 y.Range = yr 298 } 299 } 300 301 if t.Length != nil { 302 parentRange := Uint64Range 303 if y.Length != nil { 304 parentRange = y.Length 305 } 306 yr, err := parentRange.parseChildRanges(t.Length.Name, false, 0) 307 switch { 308 case err != nil: 309 errs = append(errs, fmt.Errorf("%s: bad length: %v", Source(t.Length), err)) 310 case yr.Equal(y.Length): 311 default: 312 for _, r := range yr { 313 if r.Min.Negative { 314 errs = append(errs, fmt.Errorf("%s: negative length: %v", Source(t.Length), yr)) 315 break 316 } 317 } 318 y.Length = yr 319 } 320 } 321 322 set := func(e *EnumType, name string, value *Value) error { 323 if value == nil { 324 return e.SetNext(name) 325 } 326 n, err := ParseInt(value.Name) 327 if err != nil { 328 return err 329 } 330 i, err := n.Int() 331 if err != nil { 332 return err 333 } 334 return e.Set(name, i) 335 } 336 337 if len(t.Enum) > 0 { 338 enum := NewEnumType() 339 for _, e := range t.Enum { 340 if err := set(enum, e.Name, e.Value); err != nil { 341 errs = append(errs, fmt.Errorf("%s: %v", Source(e), err)) 342 } 343 } 344 y.Enum = enum 345 } 346 347 if len(t.Bit) > 0 { 348 bit := NewBitfield() 349 for _, e := range t.Bit { 350 if err := set(bit, e.Name, e.Position); err != nil { 351 errs = append(errs, fmt.Errorf("%s: %v", Source(e), err)) 352 } 353 } 354 y.Bit = bit 355 } 356 357 // Append any newly found patterns to the end of the list of patterns. 358 // Patterns are ANDed according to section 9.4.6. If all the patterns 359 // declared by t were also declared by the type t is based on, then 360 // no patterns are added. 361 seenPatterns := map[string]bool{} 362 for _, p := range y.Pattern { 363 seenPatterns[p] = true 364 } 365 seenPOSIXPatterns := map[string]bool{} 366 for _, p := range y.POSIXPattern { 367 seenPOSIXPatterns[p] = true 368 } 369 370 // First parse out the pattern statements. 371 // These patterns are not checked because there is no support for W3C regexes by Go. 372 for _, pv := range t.Pattern { 373 if !seenPatterns[pv.Name] { 374 seenPatterns[pv.Name] = true 375 y.Pattern = append(y.Pattern, pv.Name) 376 } 377 } 378 379 // Then, parse out the posix-pattern statements, if they exist. 380 // A YANG module could make use of either or both, so we deal with each separately. 381 posixPatterns, err := MatchingExtensions(t, "openconfig-extensions", "posix-pattern") 382 if err != nil { 383 return []error{err} 384 } 385 386 checkPattern := func(n Node, p string, flags syntax.Flags) { 387 if _, err := syntax.Parse(p, flags); err != nil { 388 if re, ok := err.(*syntax.Error); ok { 389 // Error adds "error parsing regexp" to 390 // the error, re.Code is the real error. 391 err = errors.New(re.Code.String()) 392 } 393 errs = append(errs, fmt.Errorf("%s: bad pattern: %v: %s", Source(n), err, p)) 394 } 395 } 396 for _, ext := range posixPatterns { 397 checkPattern(ext, ext.Argument, syntax.POSIX) 398 if !seenPOSIXPatterns[ext.Argument] { 399 seenPOSIXPatterns[ext.Argument] = true 400 y.POSIXPattern = append(y.POSIXPattern, ext.Argument) 401 } 402 } 403 404 // I don't know of an easy way to use a type as a key to a map, 405 // so we have to check equality the hard way. 406 looking: 407 for _, ut := range t.Type { 408 errs = append(errs, ut.resolve(d)...) 409 if ut.YangType != nil { 410 for _, yt := range y.Type { 411 if ut.YangType.Equal(yt) { 412 continue looking 413 } 414 } 415 y.Type = append(y.Type, ut.YangType) 416 } 417 } 418 419 // If we changed something, we are the new root. 420 if !y.Equal(y.Root) { 421 y.Root = &y 422 } 423 424 return errs 425 }