Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codegen output rearrange #105

Merged
merged 3 commits into from
Nov 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions schema/gen/go/adjunctCfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ func (cfg *AdjunctCfg) FieldSymbolUpper(f schema.StructField) string {
return strings.Title(f.Name())
}

// Comments returns a bool for whether comments should be included in gen output or not.
func (cfg *AdjunctCfg) Comments() bool {
return true // FUTURE: okay, maybe this should be configurable :)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? It's trivial to remove comments from Go source code by just using go/parser with the comment flag disabled, and re-printing the program. Or, if the code is simple enough, a bit of sed.


func (cfg *AdjunctCfg) MaybeUsesPtr(t schema.Type) bool {
if x, ok := cfg.maybeUsesPtr[t.Name()]; ok {
return x
Expand Down
5 changes: 4 additions & 1 deletion schema/gen/go/genList.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ func (listGenerator) IsRepr() bool { return false } // hint used in some general
func (g listGenerator) EmitNativeType(w io.Writer) {
// Lists are a pretty straightforward struct enclosing a slice.
doTemplate(`
{{- if Comments -}}
// {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .ReprKind }} kind.
{{- end}}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }} struct {
x []_{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}}
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
`, w, g.AdjCfg, g)
}

Expand Down
5 changes: 4 additions & 1 deletion schema/gen/go/genMap.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ func (g mapGenerator) EmitNativeType(w io.Writer) {
// Note that the key in 'm' is *not* a pointer.
// The value in 'm' is a pointer into 't' (except when it's a maybe; maybes are already pointers).
doTemplate(`
{{- if Comments -}}
// {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .ReprKind }} kind.
{{- end}}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }} struct {
m map[_{{ .Type.KeyType | TypeSymbol }}]{{if .Type.ValueIsNullable }}Maybe{{else}}*_{{end}}{{ .Type.ValueType | TypeSymbol }}
t []_{{ .Type | TypeSymbol }}__entry
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
`, w, g.AdjCfg, g)
// - address of 'k' is used when we return keys as nodes, such as in iterators.
// Having these in the 't' slice above amortizes moving all of them to heap at once,
Expand Down
5 changes: 4 additions & 1 deletion schema/gen/go/genStruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ func (structGenerator) IsRepr() bool { return false } // hint used in some gener

func (g structGenerator) EmitNativeType(w io.Writer) {
doTemplate(`
{{- if Comments -}}
// {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .Type.Kind }} type-kind, and may be interrogated like {{ .ReprKind }} kind.
{{- end}}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }} struct {
{{- range $field := .Type.Fields}}
{{ $field | FieldSymbolLower }} _{{ $field.Type | TypeSymbol }}{{if $field.IsMaybe }}__Maybe{{end}}
{{- end}}
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
`, w, g.AdjCfg, g)
}

Expand Down
6 changes: 4 additions & 2 deletions schema/gen/go/genUnion.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ func (g unionGenerator) EmitNativeType(w io.Writer) {
// (see further comments in the EmitNodeAssemblerType function);
// and since we do it in that one case, it's just as well to do it uniformly.
doTemplate(`
{{- if Comments -}}
// {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .Type.Kind }} type-kind, and may be interrogated like {{ .ReprKind }} kind.
{{- end}}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }} struct {
{{- if (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
tag uint
Expand All @@ -55,8 +59,6 @@ func (g unionGenerator) EmitNativeType(w io.Writer) {
x _{{ .Type | TypeSymbol }}__iface
{{- end}}
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}

type _{{ .Type | TypeSymbol }}__iface interface {
_{{ .Type | TypeSymbol }}__member()
}
Expand Down
127 changes: 127 additions & 0 deletions schema/gen/go/generate.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,133 @@
package gengo

import (
"fmt"
"io"
"os"
"path/filepath"
"sort"

"github.com/ipld/go-ipld-prime/schema"
)

// Generate takes a typesystem and the adjunct config for codegen,
// and emits generated code in the given path with the given package name.
//
// All of the files produced will match the pattern "ipldsch.*.gen.go".
func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
// Emit fixed bits.
withFile(filepath.Join(pth, "ipldsch_minima.go"), func(f io.Writer) {
EmitInternalEnums(pkgName, f)
})

// Local helper function for applying generation logic to each type.
// We will end up doing this more than once because in this layout, more than one file contains part of the story for each type.
applyToEachType := func(fn func(tg TypeGenerator, w io.Writer), f io.Writer) {
// Sort the type names so we have a determinisic order; this affects output consistency.
// Any stable order would do, but we don't presently have one, so a sort is necessary.
types := ts.GetTypes()
keys := make(sortableTypeNames, 0, len(types))
for tn := range types {
keys = append(keys, tn)
}
sort.Sort(keys)
for _, tn := range keys {
switch t2 := types[tn].(type) {
case *schema.TypeBool:
fn(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code feels repetitive to me; it seems like you could do:

var gen func(...)
switch t2 := types[tn].(type) {
case *schema.TypeBool:
	gen = NewBoolReprBoolGenerator
case ...
}
fn(gen(pkgName, t2, adjCfg), f)

case *schema.TypeInt:
fn(NewIntReprIntGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeFloat:
fn(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeString:
fn(NewStringReprStringGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeBytes:
fn(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeLink:
fn(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeStruct:
switch t2.RepresentationStrategy().(type) {
case schema.StructRepresentation_Map:
fn(NewStructReprMapGenerator(pkgName, t2, adjCfg), f)
case schema.StructRepresentation_Tuple:
fn(NewStructReprTupleGenerator(pkgName, t2, adjCfg), f)
case schema.StructRepresentation_Stringjoin:
fn(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f)
default:
panic("unrecognized struct representation strategy")
}
case *schema.TypeMap:
fn(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeList:
fn(NewListReprListGenerator(pkgName, t2, adjCfg), f)
case *schema.TypeUnion:
switch t2.RepresentationStrategy().(type) {
case schema.UnionRepresentation_Keyed:
fn(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
case schema.UnionRepresentation_Kinded:
fn(NewUnionReprKindedGenerator(pkgName, t2, adjCfg), f)
default:
panic("unrecognized union representation strategy")
}
default:
panic("add more type switches here :)")
}
}
}

// Emit a file with the type table, and the golang type defns for each type.
withFile(filepath.Join(pth, "ipldsch_types.go"), func(f io.Writer) {
// Emit headers, import statements, etc.
fmt.Fprintf(f, "package %s\n\n", pkgName)
fmt.Fprintf(f, doNotEditComment+"\n\n")

// Emit the type table.
EmitTypeTable(pkgName, ts, adjCfg, f)

// Emit the type defns matching the schema types.
fmt.Fprintf(f, "\n// --- type definitions follow ---\n\n")
applyToEachType(func(tg TypeGenerator, w io.Writer) {
tg.EmitNativeType(w)
fmt.Fprintf(f, "\n")
}, f)

})

// Emit a file with all the Node/NodeBuilder/NodeAssembler boilerplate.
// Also includes typedefs for representation-level data.
// Also includes the MaybeT boilerplate.
withFile(filepath.Join(pth, "ipldsch_satisfaction.go"), func(f io.Writer) {
// Emit headers, import statements, etc.
fmt.Fprintf(f, "package %s\n\n", pkgName)
fmt.Fprintf(f, doNotEditComment+"\n\n")
fmt.Fprintf(f, "import (\n")
fmt.Fprintf(f, "\tipld \"github.com/ipld/go-ipld-prime\"\n") // referenced everywhere.
fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/node/mixins\"\n") // referenced by node implementation guts.
fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/schema\"\n") // referenced by maybes (and surprisingly little else).
fmt.Fprintf(f, ")\n\n")

// For each type, we'll emit... everything except the native type, really.
applyToEachType(func(tg TypeGenerator, w io.Writer) {
tg.EmitNativeAccessors(w)
tg.EmitNativeBuilder(w)
tg.EmitNativeMaybe(w)
EmitNode(tg, w)
tg.EmitTypedNodeMethodType(w)
tg.EmitTypedNodeMethodRepresentation(w)

nrg := tg.GetRepresentationNodeGen()
EmitNode(nrg, w)

fmt.Fprintf(f, "\n")
}, f)
})
}

// GenerateSplayed is like Generate, but emits a differnet pattern of files.
// GenerateSplayed emits many more individual files than Generate.
//
// This function should be considered deprecated and may be removed in the future.
func GenerateSplayed(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd personally remove it directly. The old code will always be in the git history if we wish to reuse it.

// Emit fixed bits.
withFile(filepath.Join(pth, "minima.go"), func(f io.Writer) {
EmitInternalEnums(pkgName, f)
Expand Down Expand Up @@ -63,6 +182,8 @@ func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctC

// Emit the unified type table.
withFile(filepath.Join(pth, "typeTable.go"), func(f io.Writer) {
fmt.Fprintf(f, "package %s\n\n", pkgName)
fmt.Fprintf(f, doNotEditComment+"\n\n")
EmitTypeTable(pkgName, ts, adjCfg, f)
})
}
Expand All @@ -75,3 +196,9 @@ func withFile(filename string, fn func(io.Writer)) {
defer f.Close()
fn(f)
}

type sortableTypeNames []schema.TypeName

func (a sortableTypeNames) Len() int { return len(a) }
func (a sortableTypeNames) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sortableTypeNames) Less(i, j int) bool { return a[i] < a[j] }
9 changes: 5 additions & 4 deletions schema/gen/go/generators.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ func EmitFileHeader(packageName string, w io.Writer) {
fmt.Fprintf(w, ")\n\n")
}

// EmitEntireType calls all methods of TypeGenerator and streams
// all results into a single writer.
// EmitEntireType is a helper function calls all methods of TypeGenerator
// and streams all results into a single writer.
// (This implies two calls to EmitNode -- one for the type-level and one for the representation-level.)
func EmitEntireType(tg TypeGenerator, w io.Writer) {
tg.EmitNativeType(w)
tg.EmitNativeAccessors(w)
Expand All @@ -108,6 +109,8 @@ func EmitEntireType(tg TypeGenerator, w io.Writer) {
EmitNode(rng, w)
}

// EmitNode is a helper function that calls all methods of NodeGenerator
// and streams all results into a single writer.
func EmitNode(ng NodeGenerator, w io.Writer) {
ng.EmitNodeType(w)
ng.EmitNodeTypeAssertions(w)
Expand Down Expand Up @@ -156,8 +159,6 @@ func EmitTypeTable(pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg, w i
// REVIEW: if "T__Repr" is how we want to expose this. We could also put 'Repr' accessors on the type/prototype objects.
// FUTURE: types and prototypes are proposed to be the same. Some of this text pretends they already are, but work is needed on this.
doTemplate(`
package `+pkgName+`

// Type is a struct embeding a NodePrototype/Type for every Node implementation in this package.
// One of its major uses is to start the construction of a value.
// You can use it like this:
Expand Down
5 changes: 4 additions & 1 deletion schema/gen/go/genpartsCommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ func emitNativeType_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) {
// while also having the advantage of meaning we can block direct casting,
// which is desirable because the compiler then ensures our validate methods can't be evaded.
doTemplate(`
type _{{ .Type | TypeSymbol }} struct{ x {{ .ReprKind | KindPrim }} }
{{- if Comments -}}
// {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .ReprKind }} kind.
{{- end}}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }} struct{ x {{ .ReprKind | KindPrim }} }
`, w, adjCfg, data)
}

Expand Down
4 changes: 4 additions & 0 deletions schema/gen/go/genpartsMinima.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
// EmitInternalEnums creates a file with enum types used internally.
// For example, the state machine values used in map and list builders.
// These always need to exist exactly once in each package created by codegen.
//
// The file header and import statements are included in the output of this function.
// (The imports in this file are different than most others in codegen output;
// we gather up any references to other packages in this file in order to simplify the rest of codegen's awareness of imports.)
func EmitInternalEnums(packageName string, w io.Writer) {
fmt.Fprint(w, wish.Dedent(`
package `+packageName+`
Expand Down
1 change: 1 addition & 0 deletions schema/gen/go/templateUtil.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func doTemplate(tmplstr string, w io.Writer, adjCfg *AdjunctCfg, data interface{
"FieldSymbolLower": adjCfg.FieldSymbolLower,
"FieldSymbolUpper": adjCfg.FieldSymbolUpper,
"MaybeUsesPtr": adjCfg.MaybeUsesPtr,
"Comments": adjCfg.Comments,

// The whole AdjunctConfig can be accessed.
// Access methods like UnionMemlayout through this, as e.g. `.AdjCfg.UnionMemlayout`.
Expand Down