Clean up Manager generation.

- Organize manager function generation into one helper method
- Vocabulary name is passed into the type & property generators
- Use interface only in the manager
- Remove unused flags in the main program
このコミットが含まれているのは:
Cory Slep 2019-01-05 16:22:37 +01:00
コミット 4f47e7fdfa
9個のファイルの変更123行の追加172行の削除

ファイルの表示

@ -199,14 +199,14 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) {
Directory: pub.WriteDir(),
})
}
// TODO: For Manager
priv := c.VocabularyRoot.PrivatePackage()
file := jen.NewFilePath(priv.Path())
file.Add(v.Manager.PrivateManager().Definition())
// Manager
pub := c.VocabularyRoot.PublicPackage()
file := jen.NewFilePath(pub.Path())
file.Add(v.Manager.Definition().Definition())
f = append(f, &File{
F: file,
FileName: "gen_manager.go",
Directory: priv.WriteDir(),
Directory: pub.WriteDir(),
})
return
}
@ -268,7 +268,7 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary) (v vocabulary, e e
}
}
v.Manager, e = props.NewManagerGenerator(
*c.VocabularyRoot,
c.VocabularyRoot.PublicPackage(),
v.typeArray(),
v.funcPropArray(),
v.nonFuncPropArray())
@ -374,6 +374,7 @@ func (c Converter) convertType(t rdf.VocabularyType,
}
}
tg, e = props.NewTypeGenerator(
v.GetName(),
pm,
name,
t.Notes,
@ -400,6 +401,7 @@ func (c Converter) convertFunctionalProperty(p rdf.VocabularyProperty,
return
}
fp = props.NewFunctionalPropertyGenerator(
v.GetName(),
pm,
c.toIdentifier(p),
p.Notes,
@ -423,6 +425,7 @@ func (c Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty,
return
}
nfp = props.NewNonFunctionalPropertyGenerator(
v.GetName(),
pm,
c.toIdentifier(p),
k,

ファイルの表示

@ -36,15 +36,10 @@ func init() {
}
var (
input = flag.String("input", "spec.json", "Input JSON-LD specification used to generate Go code.")
input = flag.String("input", "spec.json", "Input JSON-LD specification used to generate Go code.")
vocabName = flag.String("vocab", "ActivityStreams", "The vocabulary name being generated by 'input'.")
// TODO: Be more rigorous when applying this. Also, clear the default value I am using for convenience.
prefix = flag.String("prefix", "github.com/cjslep/activity/tools/exp/tmp", "Package prefix to use for all generated package paths. This should be the prefix in the GOPATH directory if generating in a subdirectory.")
// TODO: Use this flag
root = flag.String("root", "github.com/go-fed/activity/", "Go import path prefix for generated packages")
xmlpkg = flag.String("xmlpkg", "github.com/go-fed/activity/tools/exp/ref/xml", "Go package location for known XML references")
rdfpkg = flag.String("rdfpkg", "github.com/go-fed/activity/tools/exp/ref/rdf", "Go package location for known RDF references")
// TODO: Use this flag
writeWellKnown = flag.Bool("write-well-known", false, "When true, also outputs well-known specifications to './ref' subdirectories (ex: XML, RDF)")
)
type list []string
@ -60,13 +55,6 @@ func (l *list) Set(v string) error {
}
func main() {
// TODO: Use only one kind of flag style.
var ref list
var refspec list
var refpkg list
flag.Var(&ref, "ref", "Input JSON-LD specification of other referenced specifications. Must be the same size and in the same order as the 'refspec' and 'refpkg' flags")
flag.Var(&refspec, "refspec", "Base URL for other referenced specifications. Must be the same size and in the same order as the 'ref' and 'refpkg' flags")
flag.Var(&refpkg, "refpkg", "Golang package location for other referenced specifications. Must be the same size and in the same order as the 'ref' and 'refspec' flags")
flag.Parse()
// TODO: Flag validation
@ -79,7 +67,7 @@ func main() {
if err != nil {
panic(err)
}
p, err := rdf.ParseVocabulary(registry, inputJSON)
p, err := rdf.ParseVocabulary(*vocabName, registry, inputJSON)
if err != nil {
panic(err)
}
@ -97,8 +85,6 @@ func main() {
panic(err)
}
for _, file := range f {
file.F.ImportName(*xmlpkg, "xml")
file.F.ImportName(*rdfpkg, "rdf")
if e := os.MkdirAll("./"+file.Directory, 0777); e != nil {
panic(e)
}
@ -107,5 +93,4 @@ func main() {
}
}
fmt.Printf("done")
// fmt.Printf("done\n%s\n", p)
}

ファイルの表示

@ -22,13 +22,15 @@ type FunctionalPropertyGenerator struct {
//
// PropertyGenerators shoulf be in the first pass to construct, before types and
// other generators are constructed.
func NewFunctionalPropertyGenerator(pm *PackageManager,
func NewFunctionalPropertyGenerator(vocabName string,
pm *PackageManager,
name Identifier,
comment string,
kinds []Kind,
hasNaturalLanguageMap bool) *FunctionalPropertyGenerator {
return &FunctionalPropertyGenerator{
PropertyGenerator: PropertyGenerator{
vocabName: vocabName,
PackageManager: pm,
HasNaturalLanguageMap: hasNaturalLanguageMap,
Name: name,

ファイルの表示

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/cjslep/activity/tools/exp/codegen"
"github.com/dave/jennifer/jen"
"strings"
"sync"
)
@ -13,12 +12,16 @@ const (
managerInitVarName = "mgr"
)
// managerInitName returns the package variable name for the manager.
func managerInitName() string {
return managerInitVarName
}
// Generates the ActivityStreamManager that handles the static and/or dynamic
// loading of ActivityStream Core, Extended, and any extension types.
// Generates the ActivityStreamManager that handles the creation of
// ActivityStream Core, Extended, and any extension types.
//
// This is implicitly used by Application code, but Application code usually
// won't need to manually use this Manager.
//
// This also provides interfaces to break the recursive/cyclic dependencies
// between properties and types. The previous version of this tool did not
@ -39,7 +42,7 @@ func managerInitName() string {
// determine which types to actually generate, and prune the unneeded types.
// This would cut down on the bloat on a per-program basis.
type ManagerGenerator struct {
pm PackageManager
pkg Package
tg []*TypeGenerator
fp []*FunctionalPropertyGenerator
nfp []*NonFunctionalPropertyGenerator
@ -58,24 +61,18 @@ type ManagerGenerator struct {
// properties and types.
type managedMethods struct {
deserializor *codegen.Method
// TODO: Delete these?
publicDeserializor *codegen.Method
// Interface for a manager
mIface *codegen.Interface
// Interface(s) for the property/type
ifaces []*codegen.Interface
}
// NewManagerGenerator creates a new manager system.
//
// This generator should be constructed in the third pass, after types and
// property generators are all constructed.
func NewManagerGenerator(pm PackageManager,
func NewManagerGenerator(pkg Package,
tg []*TypeGenerator,
fp []*FunctionalPropertyGenerator,
nfp []*NonFunctionalPropertyGenerator) (*ManagerGenerator, error) {
mg := &ManagerGenerator{
pm: pm,
pkg: pkg,
tg: tg,
fp: fp,
nfp: nfp,
@ -87,24 +84,17 @@ func NewManagerGenerator(pm PackageManager,
// rely on already having this data available in the manager.
for _, t := range tg {
mg.tgManagedMethods[t] = &managedMethods{
// TODO: Figure out how to use this instead of the Kind abstraction
deserializor: mg.createPrivateDeserializationMethodForType(t),
publicDeserializor: nil,
mIface: nil,
deserializor: mg.createDeserializationMethodForType(t),
}
}
for _, p := range fp {
mg.fpManagedMethods[p] = &managedMethods{
deserializor: mg.createPrivateDeserializationMethodForFuncProperty(p),
publicDeserializor: nil,
mIface: nil,
deserializor: mg.createDeserializationMethodForFuncProperty(p),
}
}
for _, p := range nfp {
mg.nfpManagedMethods[p] = &managedMethods{
deserializor: mg.createPrivateDeserializationMethodForNonFuncProperty(p),
publicDeserializor: nil,
mIface: nil,
deserializor: mg.createDeserializationMethodForNonFuncProperty(p),
}
}
// Pass 2: Inform the type of this ManagerGenerator so that it can keep
@ -114,37 +104,18 @@ func NewManagerGenerator(pm PackageManager,
return nil, e
}
}
// Pass 3: Populate interfaces of the types and properties, which relies
// on the first pass's data population.
for _, t := range tg {
publicPkg := t.PublicPackage()
mg.tgManagedMethods[t].ifaces = []*codegen.Interface{t.InterfaceDefinition(publicPkg)}
}
for _, p := range fp {
publicPkg := p.GetPublicPackage()
mg.fpManagedMethods[p].ifaces = []*codegen.Interface{p.InterfaceDefinition(publicPkg)}
}
for _, p := range nfp {
publicPkg := p.GetPublicPackage()
mg.nfpManagedMethods[p].ifaces = p.InterfaceDefinitions(publicPkg)
}
return mg, nil
}
func (m *ManagerGenerator) publicManager() *codegen.Struct {
// TODO
return nil
}
func (m *ManagerGenerator) privatePackage() Package {
return m.pm.PrivatePackage()
}
func (m *ManagerGenerator) getPrivateDeserializationMethodForType(t *TypeGenerator) *codegen.Method {
// getDeserializationMethodForType obtains the deserialization method for a
// type.
func (m *ManagerGenerator) getDeserializationMethodForType(t *TypeGenerator) *codegen.Method {
return m.tgManagedMethods[t].deserializor
}
func (m *ManagerGenerator) getPrivateDeserializationMethodForProperty(p Property) *codegen.Method {
// getDeserializationMethodForProperty obtains the deserialization method for a
// property regardless whether it is functional or non-functional.
func (m *ManagerGenerator) getDeserializationMethodForProperty(p Property) *codegen.Method {
switch v := p.(type) {
case *FunctionalPropertyGenerator:
return m.fpManagedMethods[v].deserializor
@ -155,29 +126,20 @@ func (m *ManagerGenerator) getPrivateDeserializationMethodForProperty(p Property
}
}
// PrivateManager creates a manager implementation that works with the concrete
// Definition creates a manager implementation that works with the interface
// types required by the other PropertyGenerators and TypeGenerators for
// serializing and deserializing.
//
// Applications should NOT use this private manager as it will force them to
// rely on the concrete type implementations. The public version uses interfaces
// which isolates Application code from this specific go-fed implementation. If
// another alternative to go-fed were to be created, it could target those
// interfaces and be a drop-in replacement for everyone's applications.
// Applications will implicitly use this manager and be isolated from the
// underlying specific go-fed implementation. If another alternative to go-fed
// were to be created, it could target those interfaces and be a drop-in
// replacement for an application.
//
// It is necessary to acheive isolation without cyclic dependencies: types and
// properties can each belong in their own package (if desired) to minimize
// binary bloat. This is theoretical until the below TODO is tackled because
// this concrete implementation relies on linking against all of the types and
// properties to behave correctly. There is no simple way around this. But I am
// preparing for the better solution here.
//
// TODO: Analyze a given program, and only generate the types and properties in
// use by the go program.
func (m *ManagerGenerator) PrivateManager() *codegen.Struct {
// It is necessary to have this to acheive isolation without cyclic
// dependencies: types and properties can each belong in their own package (if
// desired) to minimize binary bloat.
func (m *ManagerGenerator) Definition() *codegen.Struct {
var methods []*codegen.Method
// TODO: Interface versions of these methods should also be present for
// these specific types.
for _, tg := range m.tgManagedMethods {
methods = append(methods, tg.deserializor)
}
@ -188,7 +150,7 @@ func (m *ManagerGenerator) PrivateManager() *codegen.Struct {
methods = append(methods, nfp.deserializor)
}
s := codegen.NewStruct(
jen.Commentf(fmt.Sprintf("%s privately manages concrete types and deserializations for internal use by generated code. Application code should use the public version instead, which uses interfaces to abstract away the generated code and allow apps to not entirely rely on go-fed should they choose not to.", managerName)),
jen.Commentf(fmt.Sprintf("%s manages interface types and deserializations for use by generated code. Application code implicitly uses this manager at run-time to create concrete implementations of the interfaces.", managerName)),
managerName,
methods,
/*functions=*/ nil,
@ -196,79 +158,44 @@ func (m *ManagerGenerator) PrivateManager() *codegen.Struct {
return s
}
// createPrivateDeserializationMethodForType creates a new method for the
// private manager.
//
// TODO: Unify these three methods behind some kind of interface.
func (m *ManagerGenerator) createPrivateDeserializationMethodForType(tg *TypeGenerator) *codegen.Method {
dn := tg.deserializationFnName()
pkg := m.pm.PrivatePackage()
// TODO: Better naming scheme from package name
name := fmt.Sprintf("%s%s%s", dn, tg.PrivatePackage().Name(), strings.Title(pkg.Name()))
return codegen.NewCommentedValueMethod(
pkg.Path(),
name,
managerName,
/*param=*/ nil,
[]jen.Code{
jen.Func().Params(
jen.Map(jen.String()).Interface(),
).Params(
// TODO: Qualify this.
jen.Op("*").Id(tg.TypeName()),
jen.Error(),
),
},
[]jen.Code{
jen.Return(
jen.Qual(tg.PrivatePackage().Path(), dn),
),
},
jen.Commentf("%s returns the deserialization method for the %q type in package %q", name, tg.TypeName(), tg.PrivatePackage().Name()))
// createDeserializationMethodForType creates a new deserialization method for
// a type.
func (m *ManagerGenerator) createDeserializationMethodForType(tg *TypeGenerator) *codegen.Method {
return m.createDeserializationMethod(
tg.deserializationFnName(),
tg.PublicPackage(),
tg.PrivatePackage(),
tg.InterfaceName(),
tg.VocabName())
}
// createPrivateDeserializationMethodForFuncProperty creates a new method for the
// private manager.
//
// TODO: Unify these three methods behind some kind of interface.
func (m *ManagerGenerator) createPrivateDeserializationMethodForFuncProperty(fp *FunctionalPropertyGenerator) *codegen.Method {
dn := fp.DeserializeFnName()
pkg := m.pm.PrivatePackage()
// TODO: Better naming scheme from package name
name := fmt.Sprintf("%s%s%s", dn, fp.GetPrivatePackage().Name(), strings.Title(pkg.Name()))
return codegen.NewCommentedValueMethod(
pkg.Path(),
name,
managerName,
/*param=*/ nil,
[]jen.Code{
jen.Func().Params(
jen.Map(jen.String()).Interface(),
).Params(
// TODO: Qualify this.
jen.Op("*").Id(fp.StructName()),
jen.Error(),
),
},
[]jen.Code{
jen.Return(
jen.Qual(fp.GetPrivatePackage().Path(), dn),
),
},
jen.Commentf("%s returns the deserialization method for the %q functional property in package %q", name, fp.StructName(), fp.GetPrivatePackage().Name()))
// createDeserializationMethodForFuncProperty creates a new deserialization
// method for a functional property.
func (m *ManagerGenerator) createDeserializationMethodForFuncProperty(fp *FunctionalPropertyGenerator) *codegen.Method {
return m.createDeserializationMethod(
fp.DeserializeFnName(),
fp.GetPublicPackage(),
fp.GetPrivatePackage(),
fp.InterfaceName(),
fp.VocabName())
}
// createPrivateDeserializationMethodForNonFuncProperty creates a new method for the
// private manager.
//
// TODO: Unify these three methods behind some kind of interface.
func (m *ManagerGenerator) createPrivateDeserializationMethodForNonFuncProperty(nfp *NonFunctionalPropertyGenerator) *codegen.Method {
dn := nfp.DeserializeFnName()
pkg := m.pm.PrivatePackage()
// TODO: Better naming scheme from package name
name := fmt.Sprintf("%s%s%s", dn, nfp.GetPrivatePackage().Name(), strings.Title(pkg.Name()))
// createDeserializationMethodForNonFuncProperty creates a new deserialization
// method for a non-functional property.
func (m *ManagerGenerator) createDeserializationMethodForNonFuncProperty(nfp *NonFunctionalPropertyGenerator) *codegen.Method {
return m.createDeserializationMethod(
nfp.DeserializeFnName(),
nfp.GetPublicPackage(),
nfp.GetPrivatePackage(),
nfp.InterfaceName(),
nfp.VocabName())
}
// createDeserializationMethod returns a function
func (m *ManagerGenerator) createDeserializationMethod(deserName string, pubPkg, privPkg Package, interfaceName, vocabName string) *codegen.Method {
name := fmt.Sprintf("%s%s", deserName, vocabName)
return codegen.NewCommentedValueMethod(
pkg.Path(),
m.pkg.Path(),
name,
managerName,
/*param=*/ nil,
@ -276,15 +203,28 @@ func (m *ManagerGenerator) createPrivateDeserializationMethodForNonFuncProperty(
jen.Func().Params(
jen.Map(jen.String()).Interface(),
).Params(
// TODO: Qualify this.
jen.Op("*").Id(nfp.StructName()),
jen.Qual(pubPkg.Path(), interfaceName),
jen.Error(),
),
},
[]jen.Code{
jen.Return(
jen.Qual(nfp.GetPrivatePackage().Path(), dn),
jen.Func().Params(
jen.Id("m").Map(jen.String()).Interface(),
).Params(
jen.Qual(pubPkg.Path(), interfaceName),
jen.Error(),
).Block(
jen.List(
jen.Id("i"),
jen.Err(),
).Op(":=").Qual(privPkg.Path(), deserName).Call(jen.Id("m")),
jen.Return(jen.List(
jen.Id("i"),
jen.Err(),
)),
),
),
},
jen.Commentf("%s returns the deserialization method for the %q non-functional property in package %q", name, nfp.StructName(), nfp.GetPrivatePackage().Name()))
jen.Commentf("%s returns the deserialization method for the %q non-functional property in the vocabulary %q", name, interfaceName, vocabName))
}

ファイルの表示

@ -23,12 +23,14 @@ type NonFunctionalPropertyGenerator struct {
//
// PropertyGenerators shoulf be in the first pass to construct, before types and
// other generators are constructed.
func NewNonFunctionalPropertyGenerator(pm *PackageManager,
func NewNonFunctionalPropertyGenerator(vocabName string,
pm *PackageManager,
name Identifier,
kinds []Kind,
hasNaturalLanguageMap bool) *NonFunctionalPropertyGenerator {
return &NonFunctionalPropertyGenerator{
PropertyGenerator: PropertyGenerator{
vocabName: vocabName,
PackageManager: pm,
HasNaturalLanguageMap: hasNaturalLanguageMap,
Name: name,

ファイルの表示

@ -105,6 +105,7 @@ func (k Kind) lessFnCode(this, other *jen.Statement) *jen.Statement {
//
// TODO: Make this type private
type PropertyGenerator struct {
vocabName string
// TODO: Make these private
PackageManager *PackageManager
Name Identifier
@ -114,6 +115,11 @@ type PropertyGenerator struct {
asIterator bool
}
// VocabName returns this property's vocabulary name.
func (p *PropertyGenerator) VocabName() string {
return p.vocabName
}
// GetPrivatePackage gets this property's private Package.
func (p *PropertyGenerator) GetPrivatePackage() Package {
return p.PackageManager.PrivatePackage()

ファイルの表示

@ -50,6 +50,7 @@ type Property interface {
// TypeGenerator represents an ActivityStream type definition to generate in Go.
type TypeGenerator struct {
vocabName string
pm *PackageManager
typeName string
comment string
@ -84,10 +85,11 @@ type TypeGenerator struct {
//
// A ManagerGenerator must be created with this type before Definition is
// called, to ensure that the serialization functions are properly set up.
func NewTypeGenerator(pm *PackageManager, typeName, comment string,
func NewTypeGenerator(vocabName string, pm *PackageManager, typeName, comment string,
properties, withoutProperties, rangeProperties []Property,
extends, disjoint []*TypeGenerator) (*TypeGenerator, error) {
t := &TypeGenerator{
vocabName: vocabName,
pm: pm,
typeName: typeName,
comment: comment,
@ -129,7 +131,7 @@ func (t *TypeGenerator) apply(m *ManagerGenerator) error {
// Set up Kind functions
// Note: this "i" must be the same as the "i" in the deserialization definition.
// TODO: Remove this kluge.
deser := m.getPrivateDeserializationMethodForType(t).On(managerInitName())
deser := m.getDeserializationMethodForType(t).On(managerInitName())
kind := jen.Qual(t.PublicPackage().Path(), t.InterfaceName())
for _, p := range t.rangeProperties {
if e := p.SetKindFns(t.TypeName(), kind, deser); e != nil {
@ -139,6 +141,11 @@ func (t *TypeGenerator) apply(m *ManagerGenerator) error {
return nil
}
// VocabName returns this TypeGenerator's vocabulary name.
func (t *TypeGenerator) VocabName() string {
return t.vocabName
}
// Package gets this TypeGenerator's Private Package.
func (t *TypeGenerator) PrivatePackage() Package {
return t.pm.PrivatePackage()
@ -230,7 +237,6 @@ func (t *TypeGenerator) InterfaceDefinition(pkg Package) *codegen.Interface {
// Definition generates the golang code for this ActivityStreams type.
func (t *TypeGenerator) Definition() *codegen.Struct {
t.cacheOnce.Do(func() {
// TODO: Constructor function (not here, maybe in manager?)
members := t.members()
ser := t.serializationMethod()
less := t.lessMethod()
@ -600,19 +606,18 @@ func (t *TypeGenerator) lessMethod() (less *codegen.Method) {
func (t *TypeGenerator) kindDeserializationFunc() (deser *codegen.Function) {
deserCode := jen.Commentf("Begin: Known property deserialization").Line()
for _, prop := range t.allProperties() {
deserMethod := t.m.getPrivateDeserializationMethodForProperty(prop)
deserMethod := t.m.getDeserializationMethodForProperty(prop)
deserCode = deserCode.Add(
jen.If(
jen.List(
jen.Id("p"),
jen.Err(),
// TODO: Ensure this variable is called correctly
).Op(":=").Add(deserMethod.Call(managerInitName(), jen.Id("m"))),
).Op(":=").Add(deserMethod.On(managerInitName()).Call().Call(jen.Id("m"))),
jen.Err().Op("!=").Nil(),
).Block(
jen.Return(jen.Nil(), jen.Err()),
).Else().Block(
jen.Id(codegen.This()).Dot(t.memberName(prop)).Op("=").Op("*").Id("p"),
jen.Id(codegen.This()).Dot(t.memberName(prop)).Op("=").Id("p"),
).Line())
}
deserCode = deserCode.Commentf("End: Known property deserialization").Line()

ファイルの表示

@ -42,13 +42,19 @@ func (p ParsedVocabulary) String() string {
// Vocabulary contains the type, property, and value definitions for a single
// ActivityStreams or extension vocabulary.
type Vocabulary struct {
Name string
Types map[string]VocabularyType
Properties map[string]VocabularyProperty
Values map[string]VocabularyValue
}
func (v Vocabulary) GetName() string {
return v.Name
}
func (v Vocabulary) String() string {
var b bytes.Buffer
b.WriteString(fmt.Sprintf("Vocabulary %q\n", v.Name))
for k, v := range v.Types {
b.WriteString(fmt.Sprintf("Type %s:\n\t%s\n", k, v))
}

ファイルの表示

@ -119,7 +119,7 @@ type RDFNode interface {
// ParseVocabulary parses the specified input as an ActivityStreams context that
// specifies a Core, Extended, or Extension vocabulary.
func ParseVocabulary(registry *RDFRegistry, input JSONLD) (vocabulary *ParsedVocabulary, err error) {
func ParseVocabulary(name string, registry *RDFRegistry, input JSONLD) (vocabulary *ParsedVocabulary, err error) {
var nodes []RDFNode
nodes, err = parseJSONLDContext(registry, input)
if err != nil {
@ -151,6 +151,8 @@ func ParseVocabulary(registry *RDFRegistry, input JSONLD) (vocabulary *ParsedVoc
// Step 3: Populate VocabularyType's 'Properties' and
// 'WithoutProperties' fields
err = populatePropertiesOnTypes(ctx)
// Populate this parsed vocabulary's name
vocabulary.Vocab.Name = name
return
}