From dcbba9f06cb676658d4328601fbc191bd1dcf53d Mon Sep 17 00:00:00 2001 From: Douglas Hubler Date: Tue, 4 Feb 2020 17:52:59 -0500 Subject: [PATCH] improve how typedefs, groupings and identities resolve references --- meta/builder.go | 31 ++- meta/compile.go | 74 ++++--- meta/core.go | 319 +++++++++++++++-------------- meta/core_gen.go | 64 +++--- meta/core_gen.in | 6 +- meta/core_gen_main.go | 8 +- meta/meta.go | 3 +- meta/resolver.go | 63 ++++-- meta/util.go | 76 ++----- parser/parser_test.go | 1 - parser/testdata/import/gold/x.json | 2 +- parser/testdata/import/one.yang | 8 +- 12 files changed, 339 insertions(+), 316 deletions(-) diff --git a/meta/builder.go b/meta/builder.go index a1d6359e..15179c02 100644 --- a/meta/builder.go +++ b/meta/builder.go @@ -94,15 +94,12 @@ func (b *Builder) Revision(o interface{}, rev string) *Revision { switch x := o.(type) { case *Module: r.parent = x - r.scope = x x.rev = append(x.rev, &r) case *Import: r.parent = x - r.scope = x x.rev = &r case *Include: r.parent = x - r.scope = x x.rev = &r default: b.setErr(fmt.Errorf("%T does not allow revisions, only modules, imports or includes do", o)) @@ -160,6 +157,7 @@ func (b *Builder) Grouping(o interface{}, ident string) *Grouping { if !valid { b.setErr(fmt.Errorf("%T does not support groupings", o)) } else { + g.originalParent = h.(Definition) h.addGrouping(&g) } @@ -262,8 +260,8 @@ func (b *Builder) Must(o interface{}, expression string) *Must { if !valid { b.setErr(fmt.Errorf("%T does not support must", o)) } else { + m.parent = h.(Meta) h.addMust(&m) - m.scopedParent = m.parent } return &m } @@ -327,6 +325,7 @@ func (b *Builder) Augment(o interface{}, path string) *Augment { if x, valid := o.(HasAugments); !valid { b.setErr(fmt.Errorf("%T does not allow augments", o)) } else { + a.originalParent = x.(Definition) x.addAugments(&a) } return &a @@ -408,7 +407,7 @@ func (b *Builder) Uses(o interface{}, ident string) *Uses { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h x.schemaId = b.uid b.uid++ h.addDataDefinition(&x) @@ -424,7 +423,7 @@ func (b *Builder) Container(o interface{}, ident string) *Container { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -436,7 +435,7 @@ func (b *Builder) List(o interface{}, ident string) *List { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -457,7 +456,7 @@ func (b *Builder) Leaf(o interface{}, ident string) *Leaf { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -469,7 +468,7 @@ func (b *Builder) LeafList(o interface{}, ident string) *LeafList { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -481,7 +480,7 @@ func (b *Builder) Any(o interface{}, ident string) *Any { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -494,7 +493,7 @@ func (b *Builder) Choice(o interface{}, ident string) *Choice { } if h, valid := b.parentDataDefinition(o, ident); valid { x.parent = h - x.scope = h + x.originalParent = h h.addDataDefinition(&x) } return &x @@ -509,7 +508,7 @@ func (b *Builder) Case(o interface{}, ident string) *ChoiceCase { b.setErr(fmt.Errorf("%T does not support case definitions, only choice does", o)) } else { x.parent = h - x.scope = h + x.originalParent = h h.cases[ident] = &x } return &x @@ -537,7 +536,7 @@ func (b *Builder) Action(o interface{}, ident string) *Rpc { b.setErr(fmt.Errorf("%T does not support actions", o)) } else { x.parent = h - x.scope = h + x.originalParent = h h.addAction(&x) } return &x @@ -550,7 +549,7 @@ func (b *Builder) ActionInput(o interface{}) *RpcInput { b.setErr(fmt.Errorf("%T does not support action input, only rpc or action does", o)) } else { x.parent = h - x.scope = h + x.originalParent = h h.input = &x } return &x @@ -563,7 +562,7 @@ func (b *Builder) ActionOutput(o interface{}) *RpcOutput { b.setErr(fmt.Errorf("%T does not support action output, only rpc or action does", o)) } else { x.parent = h - x.scope = h + x.originalParent = h h.output = &x } return &x @@ -578,7 +577,7 @@ func (b *Builder) Notification(o interface{}, ident string) *Notification { b.setErr(fmt.Errorf("%T does not support action output, only rpc or action does", o)) } else { x.parent = h - x.scope = h + x.originalParent = h h.addNotification(&x) } return &x diff --git a/meta/compile.go b/meta/compile.go index 39cf7bf1..15ac410a 100644 --- a/meta/compile.go +++ b/meta/compile.go @@ -55,8 +55,8 @@ func (c *compiler) compile(o interface{}) error { } } } - if x, ok := o.(Leafable); ok { - if err := c.compileType(x.Type(), x); err != nil { + if x, ok := o.(HasType); ok { + if err := c.compileType(x.Type(), x.(Leafable)); err != nil { return err } if err := c.compile(x.Type()); err != nil { @@ -187,21 +187,29 @@ func (c *compiler) identity(y *Identity) error { return nil } y.derived = make(map[string]*Identity) + + // add yourself to list y.derived[y.ident] = y + + // find all the derived identities for _, baseId := range y.derivedIds { - m, baseIdent, err := rootByIdent(y, baseId) + m := y.parent + prefix, ident := splitIdent(baseId) + m, _, err := findModuleAndIsExternal(m, prefix) if err != nil { return err } - ident, found := m.Identities()[baseIdent] + identity, found := m.Identities()[ident] if !found { return errors.New(SchemaPath(y) + " - " + baseId + " identity not found") } - y.derived[baseId] = ident - if err := c.compile(ident); err != nil { + y.derived[baseId] = identity + if err := c.compile(identity); err != nil { return err } - for subId, subIdent := range ident.Identities() { + // copy in all the other identities because they all become part of + // the same identity set + for subId, subIdent := range identity.Identities() { y.derived[subId] = subIdent } } @@ -218,7 +226,7 @@ func (c *compiler) compileType(y *Type, parent Leafable) error { var hasTypedef bool y.format, hasTypedef = val.TypeAsFormat(y.ident) if !hasTypedef { - tdef, err := c.findScopedTypedef(y, parent, y.ident) + tdef, err := c.findTypedef(y, parent, y.ident) if err != nil { return err } @@ -253,11 +261,12 @@ func (c *compiler) compileType(y *Type, parent Leafable) error { } if y.format == val.FmtIdentityRef { - m, baseIdent, err := rootByIdent(parent, y.base) + prefix, ident := splitIdent(y.base) + m, _, err := findModuleAndIsExternal(parent, prefix) if err != nil { return err } - identity, found := m.Identities()[baseIdent] + identity, found := m.Identities()[ident] if !found { return errors.New(SchemaPath(parent) + " - " + y.base + " identity not found") } @@ -313,16 +322,31 @@ func (c *compiler) compileType(y *Type, parent Leafable) error { return nil } -func (c *compiler) findScopedTypedef(y *Type, parent Meta, ident string) (*Typedef, error) { - // lazy load grouping - var found *Typedef - xMod, xIdent, err := externalModule(parent, ident) - if err != nil { - goto nomatch +func (c *compiler) findTypedef(y *Type, parent Definition, qualifiedIdent string) (*Typedef, error) { + prefix, ident := splitIdent(qualifiedIdent) + + // From RFC + // A reference to an unprefixed type or grouping, or one that uses the + // prefix of the current module, is resolved by locating the matching + // "typedef" or "grouping" statement among the immediate substatements + // of each ancestor statement. + // this means if prefix is local module, then ignore it and follow chain + searchHeirarcy := (prefix == "") + var module *Module + if !searchHeirarcy { + m, isExternal, err := findModuleAndIsExternal(parent, prefix) + if err != nil { + return nil, err + } + if !isExternal { + searchHeirarcy = true + } else { + module = m + } } - if xMod != nil { - found = xMod.Typedefs()[xIdent] - } else { + + var found *Typedef + if searchHeirarcy { p := parent for p != nil { if ptd, ok := p.(HasTypedefs); ok { @@ -330,21 +354,21 @@ func (c *compiler) findScopedTypedef(y *Type, parent Meta, ident string) (*Typed break } } - if hasScope, ok := p.(cloneable); ok { - p = hasScope.scopedParent() - } else { - p = p.Parent() - } + p = p.getOriginalParent() } + } else { + found = module.Typedefs()[ident] } -nomatch: + if found == nil { return nil, errors.New(SchemaPath(parent) + " - typedef " + y.ident + " not found") } + // this will recurse if typedef references another typedef if err := c.compile(found); err != nil { return nil, err } + return found, nil } diff --git a/meta/core.go b/meta/core.go index 0d116668..f5bf5aa6 100644 --- a/meta/core.go +++ b/meta/core.go @@ -49,6 +49,10 @@ func (y *Module) Revision() *Revision { return nil } +func (y *Module) getOriginalParent() Definition { + return nil +} + func (y *Module) RevisionHistory() []*Revision { return y.rev } @@ -147,19 +151,19 @@ func (y *Include) Revision() *Revision { } type Choice struct { - desc string - parent Meta - scope Meta - ident string - ref string - when *When - configPtr *bool - mandatoryPtr *bool - defaultVal interface{} - status Status - cases map[string]*ChoiceCase - ifs []*IfFeature - extensions []*Extension + desc string + parent Meta + originalParent Definition + ident string + ref string + when *When + configPtr *bool + mandatoryPtr *bool + defaultVal interface{} + status Status + cases map[string]*ChoiceCase + ifs []*IfFeature + extensions []*Extension } func (y *Choice) Cases() map[string]*ChoiceCase { @@ -177,24 +181,23 @@ func (y *Choice) CaseIdents() []string { } type ChoiceCase struct { - ident string - desc string - ref string - parent Meta - scope Meta - when *When - dataDefs []Definition - dataDefsIndex map[string]Definition - ifs []*IfFeature - extensions []*Extension - recursive bool + ident string + desc string + ref string + parent Meta + originalParent Definition + when *When + dataDefs []Definition + dataDefsIndex map[string]Definition + ifs []*IfFeature + extensions []*Extension + recursive bool } // Revision is like a version for a module. Format is YYYY-MM-DD and should match // the name of the file on disk when multiple revisions of a file exisits. type Revision struct { parent Meta - scope Meta ident string desc string ref string @@ -202,26 +205,26 @@ type Revision struct { } type Container struct { - parent Meta - ident string - desc string - ref string - presence string - typedefs map[string]*Typedef - groupings map[string]*Grouping - actions map[string]*Rpc - notifications map[string]*Notification - dataDefs []Definition - dataDefsIndex map[string]Definition - scope Meta - status Status - configPtr *bool - mandatoryPtr *bool - when *When - ifs []*IfFeature - musts []*Must - extensions []*Extension - recursive bool + parent Meta + originalParent Definition + ident string + desc string + ref string + presence string + typedefs map[string]*Typedef + groupings map[string]*Grouping + actions map[string]*Rpc + notifications map[string]*Notification + dataDefs []Definition + dataDefsIndex map[string]Definition + status Status + configPtr *bool + mandatoryPtr *bool + when *When + ifs []*IfFeature + musts []*Must + extensions []*Extension + recursive bool } type OrderedBy int @@ -233,7 +236,7 @@ const ( type List struct { parent Meta - scope Meta + originalParent Definition ident string desc string ref string @@ -264,26 +267,26 @@ func (y *List) KeyMeta() (keyMeta []Leafable) { } type Leaf struct { - parent Meta - scope Meta - ident string - desc string - ref string - units string - configPtr *bool - mandatoryPtr *bool - defaultVal interface{} - dtype *Type - when *When - ifs []*IfFeature - musts []*Must - extensions []*Extension + parent Meta + originalParent Definition + ident string + desc string + ref string + units string + configPtr *bool + mandatoryPtr *bool + defaultVal interface{} + dtype *Type + when *When + ifs []*IfFeature + musts []*Must + extensions []*Extension } type LeafList struct { ident string parent Meta - scope Meta + originalParent Definition desc string ref string units string @@ -304,17 +307,17 @@ type LeafList struct { var anyType = newType("any") type Any struct { - ident string - desc string - ref string - parent Meta - scope Meta - configPtr *bool - mandatoryPtr *bool - when *When - ifs []*IfFeature - musts []*Must - extensions []*Extension + ident string + desc string + ref string + parent Meta + originalParent Definition + configPtr *bool + mandatoryPtr *bool + when *When + ifs []*IfFeature + musts []*Must + extensions []*Extension } func (y *Any) HasDefault() bool { @@ -357,16 +360,17 @@ func (y *Any) setUnits(string) { are applied to the grouping itself. */ type Grouping struct { - ident string - parent Meta - desc string - ref string - typedefs map[string]*Typedef - groupings map[string]*Grouping - dataDefs []Definition - dataDefsIndex map[string]Definition - actions map[string]*Rpc - notifications map[string]*Notification + ident string + parent Meta + originalParent Definition + desc string + ref string + typedefs map[string]*Typedef + groupings map[string]*Grouping + dataDefs []Definition + dataDefsIndex map[string]Definition + actions map[string]*Rpc + notifications map[string]*Notification // see RFC7950 Sec 14 // no details (config, mandatory) @@ -376,17 +380,17 @@ type Grouping struct { } type Uses struct { - ident string - desc string - ref string - parent Meta - scope Meta - schemaId interface{} - refines []*Refine - when *When - ifs []*IfFeature - augments []*Augment - extensions []*Extension + ident string + desc string + ref string + parent Meta + originalParent Definition + schemaId interface{} + refines []*Refine + when *When + ifs []*IfFeature + augments []*Augment + extensions []*Extension } func (y *Uses) Refinements() []*Refine { @@ -419,17 +423,17 @@ func (y *Refine) splitIdent() (string, string) { } type RpcInput struct { - parent Meta - scope Meta - desc string - ref string - typedefs map[string]*Typedef - groupings map[string]*Grouping - dataDefs []Definition - dataDefsIndex map[string]Definition - ifs []*IfFeature - musts []*Must - extensions []*Extension + parent Meta + originalParent Definition + desc string + ref string + typedefs map[string]*Typedef + groupings map[string]*Grouping + dataDefs []Definition + dataDefsIndex map[string]Definition + ifs []*IfFeature + musts []*Must + extensions []*Extension } func (y *RpcInput) Ident() string { @@ -437,17 +441,17 @@ func (y *RpcInput) Ident() string { } type RpcOutput struct { - parent Meta - scope Meta - desc string - ref string - typedefs map[string]*Typedef - groupings map[string]*Grouping - dataDefs []Definition - dataDefsIndex map[string]Definition - ifs []*IfFeature - musts []*Must - extensions []*Extension + parent Meta + originalParent Definition + desc string + ref string + typedefs map[string]*Typedef + groupings map[string]*Grouping + dataDefs []Definition + dataDefsIndex map[string]Definition + ifs []*IfFeature + musts []*Must + extensions []*Extension } func (y *RpcOutput) Ident() string { @@ -455,17 +459,17 @@ func (y *RpcOutput) Ident() string { } type Rpc struct { - ident string - parent Meta - scope Meta - desc string - ref string - typedefs map[string]*Typedef - groupings map[string]*Grouping - input *RpcInput - output *RpcOutput - ifs []*IfFeature - extensions []*Extension + ident string + parent Meta + originalParent Definition + desc string + ref string + typedefs map[string]*Typedef + groupings map[string]*Grouping + input *RpcInput + output *RpcOutput + ifs []*IfFeature + extensions []*Extension } func (y *Rpc) Input() *RpcInput { @@ -477,42 +481,44 @@ func (y *Rpc) Output() *RpcOutput { } type Notification struct { - ident string - parent Meta - scope Meta - desc string - ref string - typedefs map[string]*Typedef - groupings map[string]*Grouping - dataDefs []Definition - dataDefsIndex map[string]Definition - ifs []*IfFeature - extensions []*Extension + ident string + parent Meta + originalParent Definition + desc string + ref string + typedefs map[string]*Typedef + groupings map[string]*Grouping + dataDefs []Definition + dataDefsIndex map[string]Definition + ifs []*IfFeature + extensions []*Extension } type Typedef struct { - ident string - parent Meta - desc string - ref string - units string - defaultVal interface{} - dtype *Type - extensions []*Extension + ident string + parent Meta + originalParent Definition + desc string + ref string + units string + defaultVal interface{} + dtype *Type + extensions []*Extension } type Augment struct { - ident string - parent Meta - desc string - ref string - actions map[string]*Rpc - notifications map[string]*Notification - dataDefs []Definition - dataDefsIndex map[string]Definition - when *When - ifs []*IfFeature - extensions []*Extension + ident string + parent Meta + originalParent Definition + desc string + ref string + actions map[string]*Rpc + notifications map[string]*Notification + dataDefs []Definition + dataDefsIndex map[string]Definition + when *When + ifs []*IfFeature + extensions []*Extension } type AddDeviate struct { @@ -856,7 +862,6 @@ type Must struct { ref string errorMessage string errorAppTag string - scopedParent Meta expr string extensions []*Extension } diff --git a/meta/core_gen.go b/meta/core_gen.go index c5c0d74e..93706289 100644 --- a/meta/core_gen.go +++ b/meta/core_gen.go @@ -381,8 +381,8 @@ func (m *Choice) setDefault(d interface{}) { m.defaultVal = d } -func (m *Choice) scopedParent() Meta { - return m.scope +func (m *Choice) getOriginalParent() Definition { + return m.originalParent } func (m *Choice) clone(parent Meta) interface{} { @@ -516,8 +516,8 @@ func (m *ChoiceCase) Definition(ident string) Definition { return nil } -func (m *ChoiceCase) scopedParent() Meta { - return m.scope +func (m *ChoiceCase) getOriginalParent() Definition { + return m.originalParent } func (m *ChoiceCase) clone(parent Meta) interface{} { @@ -577,10 +577,6 @@ func (m *Revision) addExtension(extension *Extension) { } -func (m *Revision) scopedParent() Meta { - return m.scope -} - // Ident is identity of Container @@ -819,8 +815,8 @@ func (m *Container) setPresence(p string) { m.presence = p } -func (m *Container) scopedParent() Meta { - return m.scope +func (m *Container) getOriginalParent() Definition { + return m.originalParent } func (m *Container) clone(parent Meta) interface{} { @@ -1146,8 +1142,8 @@ func (m *List) setUnique(unique [][]string) { m.unique = unique } -func (m *List) scopedParent() Meta { - return m.scope +func (m *List) getOriginalParent() Definition { + return m.originalParent } func (m *List) clone(parent Meta) interface{} { @@ -1311,8 +1307,8 @@ func (m *Leaf) setDefault(d interface{}) { m.defaultVal = d } -func (m *Leaf) scopedParent() Meta { - return m.scope +func (m *Leaf) getOriginalParent() Definition { + return m.originalParent } func (m *Leaf) clone(parent Meta) interface{} { @@ -1507,8 +1503,8 @@ func (m *LeafList) setDefault(d interface{}) { m.defaultVal = d } -func (m *LeafList) scopedParent() Meta { - return m.scope +func (m *LeafList) getOriginalParent() Definition { + return m.originalParent } func (m *LeafList) clone(parent Meta) interface{} { @@ -1620,8 +1616,8 @@ func (m *Any) IsMandatorySet() bool { return m.mandatoryPtr != nil } -func (m *Any) scopedParent() Meta { - return m.scope +func (m *Any) getOriginalParent() Definition { + return m.originalParent } func (m *Any) clone(parent Meta) interface{} { @@ -1803,6 +1799,10 @@ func (m *Grouping) Definition(ident string) Definition { return nil } +func (m *Grouping) getOriginalParent() Definition { + return m.originalParent +} + func (m *Grouping) clone(parent Meta) interface{} { copy := *m copy.parent = parent @@ -1901,8 +1901,8 @@ func (m *Uses) setWhen(w *When) { m.when = w } -func (m *Uses) scopedParent() Meta { - return m.scope +func (m *Uses) getOriginalParent() Definition { + return m.originalParent } func (m *Uses) clone(parent Meta) interface{} { @@ -2209,8 +2209,8 @@ func (m *RpcInput) Definition(ident string) Definition { return nil } -func (m *RpcInput) scopedParent() Meta { - return m.scope +func (m *RpcInput) getOriginalParent() Definition { + return m.originalParent } func (m *RpcInput) clone(parent Meta) interface{} { @@ -2378,8 +2378,8 @@ func (m *RpcOutput) Definition(ident string) Definition { return nil } -func (m *RpcOutput) scopedParent() Meta { - return m.scope +func (m *RpcOutput) getOriginalParent() Definition { + return m.originalParent } func (m *RpcOutput) clone(parent Meta) interface{} { @@ -2479,8 +2479,8 @@ func (m *Rpc) addIfFeature(i *IfFeature) { m.ifs = append(m.ifs, i) } -func (m *Rpc) scopedParent() Meta { - return m.scope +func (m *Rpc) getOriginalParent() Definition { + return m.originalParent } func (m *Rpc) clone(parent Meta) interface{} { @@ -2630,8 +2630,8 @@ func (m *Notification) Definition(ident string) Definition { return nil } -func (m *Notification) scopedParent() Meta { - return m.scope +func (m *Notification) getOriginalParent() Definition { + return m.originalParent } func (m *Notification) clone(parent Meta) interface{} { @@ -2719,6 +2719,10 @@ func (m *Typedef) setDefault(d interface{}) { m.defaultVal = d } +func (m *Typedef) getOriginalParent() Definition { + return m.originalParent +} + // Ident is identity of Augment @@ -2878,6 +2882,10 @@ func (m *Augment) Definition(ident string) Definition { return nil } +func (m *Augment) getOriginalParent() Definition { + return m.originalParent +} + func (m *Augment) clone(parent Meta) interface{} { copy := *m copy.parent = parent diff --git a/meta/core_gen.in b/meta/core_gen.in index c68cda09..194f1b4b 100644 --- a/meta/core_gen.in +++ b/meta/core_gen.in @@ -419,9 +419,9 @@ func (m *{{.Name}}) setDefault(d interface{}) { } {{end}} -{{- if .Scope}} -func (m *{{.Name}}) scopedParent() Meta { - return m.scope +{{- if .OriginalParent}} +func (m *{{.Name}}) getOriginalParent() Definition { + return m.originalParent } {{end}} diff --git a/meta/core_gen_main.go b/meta/core_gen_main.go index d27720c5..fd9f4b3f 100644 --- a/meta/core_gen_main.go +++ b/meta/core_gen_main.go @@ -61,7 +61,7 @@ type elem struct { Unbounded bool Default bool Type bool - Scope bool + OriginalParent bool Recursable bool Clone bool Augments bool @@ -151,8 +151,8 @@ func (v *visitor) Visit(n ast.Node) ast.Visitor { v.elem.Unbounded = true case "dtype": v.elem.Type = true - case "scope": - v.elem.Scope = true + case "originalParent": + v.elem.OriginalParent = true case "recursive": v.elem.Recursable = true case "augments": @@ -168,7 +168,7 @@ func (v *visitor) Visit(n ast.Node) ast.Visitor { case "orderedBy": v.elem.OrderedBy = true case "errorMessage": - // assumes error-app-message too + // assumes error-app-tag too v.elem.ErrorMessage = true } } diff --git a/meta/meta.go b/meta/meta.go index 77fb1597..4a94e70f 100644 --- a/meta/meta.go +++ b/meta/meta.go @@ -22,8 +22,6 @@ type HasExtensions interface { } type cloneable interface { - scopedParent() Meta - clone(parent Meta) interface{} } @@ -58,6 +56,7 @@ type Describable interface { type Definition interface { Meta Identifiable + getOriginalParent() Definition } type HasPresence interface { diff --git a/meta/resolver.go b/meta/resolver.go index a79a04e2..0cf6541a 100644 --- a/meta/resolver.go +++ b/meta/resolver.go @@ -342,9 +342,9 @@ func (r *resolver) addDataDef(parent HasDataDefinitions, child Definition) (bool } if u, isUses := child.(*Uses); isUses { - g := r.findGrouping(u) - if g == nil { - return false, fmt.Errorf("%s - %s group not found", SchemaPath(u), u.ident) + g, err := r.findGrouping(u) + if err != nil { + return false, err } if master, recursionDetected := r.inProgressUses[u.schemaId]; recursionDetected { // resolve this uses later @@ -356,7 +356,7 @@ func (r *resolver) addDataDef(parent HasDataDefinitions, child Definition) (bool // resolve all children groupDefs := r.cloneDefs(parent, g.DataDefinitions(), u.when) - err := r.dataDef(parent, groupDefs) + err = r.dataDef(parent, groupDefs) if err != nil { return false, err } @@ -433,28 +433,49 @@ func (r *resolver) cloneDefs(parent HasDataDefinitions, defs []Definition, when return copy } -func (r *resolver) findGrouping(y *Uses) *Grouping { - // lazy load grouping - if xMod, xIdent, err := externalModule(y, y.ident); err != nil { - return nil - } else if xMod != nil { - return xMod.Groupings()[xIdent] - } else { - p := y.scopedParent() +func (r *resolver) findGrouping(y *Uses) (*Grouping, error) { + prefix, ident := splitIdent(y.Ident()) + + // From RFC + // A reference to an unprefixed type or grouping, or one that uses the + // prefix of the current module, is resolved by locating the matching + // "typedef" or "grouping" statement among the immediate substatements + // of each ancestor statement. + // this means if prefix is local module, then ignore it and follow chain + searchHeirarcy := (prefix == "") + var module *Module + if !searchHeirarcy { + m, isExternal, err := findModuleAndIsExternal(y, prefix) + if err != nil { + return nil, err + } + if !isExternal { + searchHeirarcy = true + } else { + module = m + } + } + + var found *Grouping + if searchHeirarcy { + var p Definition + p = y for p != nil { - if hasGroups, ok := p.(HasGroupings); ok { - if g, found := hasGroups.Groupings()[y.ident]; found { - return g + if ptd, ok := p.(HasGroupings); ok { + if found = ptd.Groupings()[ident]; found != nil { + return found, nil } } - if hasScoped, ok := p.(cloneable); ok { - p = hasScoped.scopedParent() - } else { - p = p.Parent() - } + p = p.getOriginalParent() } + } else { + found = module.Groupings()[ident] } - return nil + + if found == nil { + return nil, fmt.Errorf("%s - %s group not found", SchemaPath(y), y.Ident()) + } + return found, nil } func (r *resolver) applyRefinements(u *Uses, parent Definition) error { diff --git a/meta/util.go b/meta/util.go index fb27ba40..e5bdb730 100644 --- a/meta/util.go +++ b/meta/util.go @@ -34,69 +34,33 @@ func RootModule(m Meta) *Module { return candidate.(*Module) } -// Given ident -// foo:bar -// return the module names foo and the string "bar" -// Given -// bar -// return the local module and return back "bar" -func rootByIdent(y Meta, ident string) (*Module, string, error) { - if c, ok := y.(cloneable); ok { - y = c.scopedParent() +// Module a definition was defined in, not the module it ended up in. +// this is useful for resolving typedefs and uses +func originalModule(m Definition) *Module { + for { + if mod, isMod := m.(*Module); isMod { + return mod + } + m = m.(Definition).getOriginalParent() } - mod := RootModule(y) - i := strings.IndexRune(ident, ':') - if i < 0 { - return mod, ident, nil - } - subName := ident[:i] - sub, found := mod.imports[subName] - if !found { - return nil, "", errors.New("module not found in ident " + ident) - } - return sub.module, ident[i+1:], nil } -// Given ident -// foo:bar -// return the module names foo and the string "bar" -// Given -// bar -// return nil as this is not an external module -func externalModule(y Meta, ident string) (*Module, string, error) { +func splitIdent(ident string) (string, string) { i := strings.IndexRune(ident, ':') if i < 0 { - return nil, "", nil + return "", ident } - if c, ok := y.(cloneable); ok { - y = c.scopedParent() + return ident[:i], ident[i+1:] +} + +func findModuleAndIsExternal(y Definition, prefix string) (*Module, bool, error) { + m := originalModule(y) + if prefix == "" || m.Prefix() == prefix { + return m, false, nil } - mod := RootModule(y) - subName := ident[:i] - sub, found := mod.imports[subName] + sub, found := m.imports[prefix] if !found { - return nil, "", errors.New("module not found in ident " + ident) + return nil, true, errors.New("module not found " + prefix) } - return sub.module, ident[i+1:], nil + return sub.module, true, nil } - -// FindDefinition can return action, notification or any of the data definitions -// like container, leaf, list etc. -// func Def(parent interface{}, ident string) Definition { -// if x, ok := parent.(HasDataDefinitions); ok { -// if def := x.Definition(ident); def != nil { -// return def -// } -// } -// if x, ok := parent.(HasActions); ok { -// if def, found := x.Actions()[ident]; found { -// return def -// } -// } -// if x, ok := parent.(HasNotifications); ok { -// if def, found := x.Notifications()[ident]; found { -// return def -// } -// } -// return nil -// } diff --git a/parser/parser_test.go b/parser/parser_test.go index 5d1879db..b057c76b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -96,7 +96,6 @@ var yangTestFiles = []struct { {"/grouping", "refine"}, {"/grouping", "augment"}, {"/grouping", "empty"}, - {"/extension", "x"}, {"/extension", "y"}, diff --git a/parser/testdata/import/gold/x.json b/parser/testdata/import/gold/x.json index e3acf426..f1c6c4bf 100644 --- a/parser/testdata/import/gold/x.json +++ b/parser/testdata/import/gold/x.json @@ -14,7 +14,7 @@ "ident":"y", "leaf":{ "type":{ - "ident":"string", + "ident":"uno:y-tdef", "format":"string"}}}, { "ident":"z", diff --git a/parser/testdata/import/one.yang b/parser/testdata/import/one.yang index b75c9d49..3e1d3a9e 100644 --- a/parser/testdata/import/one.yang +++ b/parser/testdata/import/one.yang @@ -1,11 +1,15 @@ module one { - prefix ""; + prefix "uno"; namespace ""; revision 0; + typedef y-tdef { + type string; + } + grouping g { leaf y { - type string; + type uno:y-tdef; } } } \ No newline at end of file