From c3b9686d1feb725f64d99d33a71b43b7b96c003c Mon Sep 17 00:00:00 2001 From: Cory Slep Date: Fri, 18 Jan 2019 21:22:07 +0100 Subject: [PATCH] Fix init generation for multiple vocabularies. --- tools/exp/convert/convert.go | 137 +++++++++++++++++++++++++---------- tools/exp/gen/pkg.go | 24 ++++-- 2 files changed, 115 insertions(+), 46 deletions(-) diff --git a/tools/exp/convert/convert.go b/tools/exp/convert/convert.go index 3f714ef..b89ad6b 100644 --- a/tools/exp/convert/convert.go +++ b/tools/exp/convert/convert.go @@ -44,6 +44,49 @@ func newVocabulary() vocabulary { } } +// allTypeArray converts all Types, including referenced Types, to an array. +func (v vocabulary) allTypeArray() []*gen.TypeGenerator { + var typeArray []*gen.TypeGenerator + for _, ref := range v.References { + typeArray = append(typeArray, ref.typeArray()...) + } + typeArray = append(typeArray, v.typeArray()...) + return typeArray +} + +// allPropArray converts all Properties, including referenced Properties, to an +// array. +func (v vocabulary) allPropArray() []*gen.PropertyGenerator { + var propArray []*gen.PropertyGenerator + for _, ref := range v.References { + propArray = append(propArray, ref.propArray()...) + } + propArray = append(propArray, v.propArray()...) + return propArray +} + +// allFuncPropArray converts all FProps, including referenced Properties, to an +// array. +func (v vocabulary) allFuncPropArray() []*gen.FunctionalPropertyGenerator { + var funcPropArray []*gen.FunctionalPropertyGenerator + for _, ref := range v.References { + funcPropArray = append(funcPropArray, ref.funcPropArray()...) + } + funcPropArray = append(funcPropArray, v.funcPropArray()...) + return funcPropArray +} + +// allNonFuncPropArray converts all NFProps, including referenced Properties, to +// an array. +func (v vocabulary) allNonFuncPropArray() []*gen.NonFunctionalPropertyGenerator { + var nonFuncPropArray []*gen.NonFunctionalPropertyGenerator + for _, ref := range v.References { + nonFuncPropArray = append(nonFuncPropArray, ref.nonFuncPropArray()...) + } + nonFuncPropArray = append(nonFuncPropArray, v.nonFuncPropArray()...) + return nonFuncPropArray +} + // typeArray converts Types to an array. func (v vocabulary) typeArray() []*gen.TypeGenerator { tg := make([]*gen.TypeGenerator, 0, len(v.Types)) @@ -202,11 +245,8 @@ func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { // convertToFiles takes the generators for a vocabulary and maps them into a // file structure. func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { - // Values -- include all referenced values too. - for _, v := range v.Values { - pkg := c.valuePackage(v) - f = append(f, convertValue(pkg, v)) - } + pub := c.GenRoot.PublicPackage() + // References for _, ref := range v.References { for _, v := range ref.Values { pkg := c.valuePackage(v) @@ -218,6 +258,22 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { return } f = append(f, files...) + files, e = c.rootFiles(pub, ref.Name, *ref, v.Manager) + if e != nil { + return + } + f = append(f, files...) + pkgFiles, err := c.packageFiles(*ref) + if err != nil { + e = err + return + } + f = append(f, pkgFiles...) + } + // This vocabulary + for _, v := range v.Values { + pkg := c.valuePackage(v) + f = append(f, convertValue(pkg, v)) } var files []*File files, e = c.toFiles(v) @@ -225,26 +281,32 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { return } f = append(f, files...) + files, e = c.rootFiles(pub, v.Name, v, v.Manager) + if e != nil { + return + } + f = append(f, files...) pkgFiles, err := c.packageFiles(v) if err != nil { e = err return } f = append(f, pkgFiles...) - // Manager - pub := c.GenRoot.PublicPackage() - file := jen.NewFilePath(pub.Path()) - file.Add(v.Manager.Definition().Definition()) - f = append(f, &File{ - F: file, - FileName: "gen_manager.go", - Directory: pub.WriteDir(), - }) - files, e = c.rootFiles(pub, v.Name, v) + // Init file + var file *File + file, e = c.initFile(pub, v) if e != nil { return } - f = append(f, files...) + f = append(f, file) + // Manager + jenFile := jen.NewFilePath(pub.Path()) + jenFile.Add(v.Manager.Definition().Definition()) + f = append(f, &File{ + F: jenFile, + FileName: "gen_manager.go", + Directory: pub.WriteDir(), + }) // Root Package Documentation rootDocFile := jen.NewFilePath(pub.Path()) rootDocFile.PackageComment(gen.GenRootPackageComment(pub.Name())) @@ -319,22 +381,11 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*v // convertGenRoot creates code-wide code generators. func (c Converter) convertGenRoot(v *vocabulary) (e error) { - var typeArray []*gen.TypeGenerator - var funcPropArray []*gen.FunctionalPropertyGenerator - var nonFuncPropArray []*gen.NonFunctionalPropertyGenerator - for _, ref := range v.References { - typeArray = append(typeArray, ref.typeArray()...) - funcPropArray = append(funcPropArray, ref.funcPropArray()...) - nonFuncPropArray = append(nonFuncPropArray, ref.nonFuncPropArray()...) - } - typeArray = append(typeArray, v.typeArray()...) - funcPropArray = append(funcPropArray, v.funcPropArray()...) - nonFuncPropArray = append(nonFuncPropArray, v.nonFuncPropArray()...) v.Manager, e = gen.NewManagerGenerator( c.GenRoot.PublicPackage(), - typeArray, - funcPropArray, - nonFuncPropArray) + v.allTypeArray(), + v.allFuncPropArray(), + v.allNonFuncPropArray()) return } @@ -762,16 +813,9 @@ func (c Converter) packageManager(s, vocabName string) (pkg *gen.PackageManager, // rootFiles creates files that are applied for all vocabularies. These files // are the ones typically used by developers. -func (c Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary) (f []*File, e error) { +func (c Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary, m *gen.ManagerGenerator) (f []*File, e error) { pg := gen.NewPackageGenerator() - ctors, ext, disj, extBy, globalVar, initFn := pg.RootDefinitions(vocabName, v.Manager, v.typeArray(), v.propArray()) - initFile := jen.NewFilePath(pkg.Path()) - initFile.Add(globalVar).Line().Add(initFn.Definition()).Line() - f = append(f, &File{ - F: initFile, - FileName: "gen_init.go", - Directory: pkg.WriteDir(), - }) + ctors, ext, disj, extBy := pg.RootDefinitions(vocabName, m, v.typeArray(), v.propArray()) lowerVocabName := strings.ToLower(vocabName) f = append(f, funcsToFile(pkg, ctors, fmt.Sprintf("gen_pkg_%s_constructors.go", lowerVocabName))) f = append(f, funcsToFile(pkg, ext, fmt.Sprintf("gen_pkg_%s_extends.go", lowerVocabName))) @@ -780,6 +824,21 @@ func (c Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary) (f return } +// initFile creates the file with the init function that hooks together the +// runtime Manager. +func (c Converter) initFile(pkg gen.Package, root vocabulary) (f *File, e error) { + pg := gen.NewPackageGenerator() + globalVar, initFn := pg.InitDefinitions(pkg, root.allTypeArray(), root.allPropArray()) + initFile := jen.NewFilePath(pkg.Path()) + initFile.Add(globalVar).Line().Add(initFn.Definition()).Line() + f = &File{ + F: initFile, + FileName: "gen_init.go", + Directory: pkg.WriteDir(), + } + return +} + // packageFiles generates package-level files necessary for types and properties // within a single vocabulary. // diff --git a/tools/exp/gen/pkg.go b/tools/exp/gen/pkg.go index 14ff2bd..6fe68cf 100644 --- a/tools/exp/gen/pkg.go +++ b/tools/exp/gen/pkg.go @@ -171,8 +171,12 @@ func NewPackageGenerator() *PackageGenerator { return &PackageGenerator{} } +func (t *PackageGenerator) InitDefinitions(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globalManager *jen.Statement, init *codegen.Function) { + return genInit(pkg, tgs, pgs) +} + // RootDefinitions creates functions needed at the root level of the package declarations. -func (t *PackageGenerator) RootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator, pgs []*PropertyGenerator) (ctors, ext, disj, extBy []*codegen.Function, globalManager *jen.Statement, init *codegen.Function) { +func (t *PackageGenerator) RootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator, pgs []*PropertyGenerator) (ctors, ext, disj, extBy []*codegen.Function) { return rootDefinitions(vocabName, m, tgs, pgs) } @@ -245,7 +249,7 @@ func publicTypeDefinitions(tgs []*TypeGenerator) (typeI *codegen.Interface) { // rootDefinitions creates common functions needed at the root level of the // package declarations. -func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator, pgs []*PropertyGenerator) (ctors, ext, disj, extBy []*codegen.Function, globalManager *jen.Statement, init *codegen.Function) { +func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator, pgs []*PropertyGenerator) (ctors, ext, disj, extBy []*codegen.Function) { // Type constructors for _, tg := range tgs { ctors = append(ctors, codegen.NewCommentedFunction( @@ -308,17 +312,23 @@ func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator }, fmt.Sprintf("%s returns true if the other's type extends from %s.", name, tg.TypeName()))) } + return +} + +// init generates the code that implements the init calls per-type and +// per-property package, so that the Manager is injected at runtime. +func genInit(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globalManager *jen.Statement, init *codegen.Function) { // init - globalManager = jen.Var().Id(managerInitName()).Op("*").Qual(m.pkg.Path(), managerName) + globalManager = jen.Var().Id(managerInitName()).Op("*").Qual(pkg.Path(), managerName) callInitsMap := make(map[string]jen.Code, len(tgs)) for _, tg := range tgs { callInitsMap[tg.PrivatePackage().Path()] = jen.Qual(tg.PrivatePackage().Path(), setManagerFunctionName).Call( - jen.Qual(m.pkg.Path(), managerInitName()), + jen.Qual(pkg.Path(), managerInitName()), ) } for _, pg := range pgs { callInitsMap[pg.GetPrivatePackage().Path()] = jen.Qual(pg.GetPrivatePackage().Path(), setManagerFunctionName).Call( - jen.Qual(m.pkg.Path(), managerInitName()), + jen.Qual(pkg.Path(), managerInitName()), ) } callInits := make([]jen.Code, 0, len(callInitsMap)) @@ -326,12 +336,12 @@ func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator callInits = append(callInits, c) } init = codegen.NewCommentedFunction( - m.pkg.Path(), + pkg.Path(), "init", /*params=*/ nil, /*ret=*/ nil, append([]jen.Code{ - jen.Qual(m.pkg.Path(), managerInitName()).Op("=").Op("&").Qual(m.pkg.Path(), managerName).Values(), + jen.Qual(pkg.Path(), managerInitName()).Op("=").Op("&").Qual(pkg.Path(), managerName).Values(), }, callInits...), fmt.Sprintf("init handles the 'magic' of creating a %s and dependency-injecting it into every other code-generated package. This gives the implementations access to create any type needed to deserialize, without relying on the other specific concrete implementations. In order to replace a go-fed created type with your own, be sure to have the manager call your own implementation's deserialize functions instead of the built-in type. Finally, each implementation views the %s as an interface with only a subset of funcitons available. This means this %s implements the union of those interfaces.", managerName, managerName, managerName)) return