Commit 0ddeba4d authored by Eric Myhre's avatar Eric Myhre

Most of union type-level read implemented, for both style of memory layouts.

Outline for keyed representation, but only enough to get some more
things compilable and start getting all the templates exercised.

Still can't really e2e test the things we've got without getting
the rest of it and its builders finished.  It's coming along pretty
reasonably, though, it seems.
parent a9b5a196
......@@ -41,12 +41,12 @@ func (g unionGenerator) EmitNativeType(w io.Writer) {
// The interface *mostly* isn't used... except for in the return type of a speciated function which can be used to do golang-native type switches.
doTemplate(`
type _{{ .Type | TypeSymbol }} struct {
{{- with (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
{{- range $member := .Type.Members }}
x_{{ $member.Name }} _{{ $member | TypeSymbol }}
{{- end}}
{{- if (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
tag uint
{{- range $i, $member := .Type.Members }}
x{{ $i }} _{{ $member | TypeSymbol }}
{{- end}}
{{- with (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
{{- else if (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
x _{{ .Type | TypeSymbol }}__iface
{{- end}}
}
......@@ -57,7 +57,179 @@ func (g unionGenerator) EmitNativeType(w io.Writer) {
}
{{- range $member := .Type.Members }}
func (_{{ $member | TypeSymbol }}) _{{ .dot.Type | TypeSymbol }}__member() {}
func (_{{ $member | TypeSymbol }}) _{{ dot.Type | TypeSymbol }}__member() {}
{{- end}}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNativeAccessors(w io.Writer) {
doTemplate(`
func (n _{{ .Type | TypeSymbol }}) AsInterface() _{{ .Type | TypeSymbol }}__iface {
{{- if (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
switch n.tag {
{{- range $i, $member := .Type.Members }}
case {{ $i }}:
return &n.x{{ $i }}
{{- end}}
default:
panic("invalid union state; how did you create this object?")
}
{{- else if (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
return n.x
{{- end}}
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNativeBuilder(w io.Writer) {
// Unclear as yet what should go here.
}
func (g unionGenerator) EmitNativeMaybe(w io.Writer) {
emitNativeMaybe(w, g.AdjCfg, g)
}
// --- type info --->
func (g unionGenerator) EmitTypeConst(w io.Writer) {
doTemplate(`
// TODO EmitTypeConst
`, w, g.AdjCfg, g)
}
// --- TypedNode interface satisfaction --->
func (g unionGenerator) EmitTypedNodeMethodType(w io.Writer) {
doTemplate(`
func ({{ .Type | TypeSymbol }}) Type() schema.Type {
return nil /*TODO:typelit*/
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) {
emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g)
}
// --- Node interface satisfaction --->
func (g unionGenerator) EmitNodeType(w io.Writer) {
// No additional types needed. Methods all attach to the native type.
// We do, however, want some constants for our member names;
// they'll make iterators able to work faster. So let's emit those.
// These are a bit perplexing, because they're... type names.
// However, oddly enough, we don't have type names available *as nodes* anywhere else centrally available,
// so... we generate some values for them here with scoped identifers and get on with it.
// Maybe this could be elided with future work.
doTemplate(`
var (
{{- range $member := .Type.Members }}
memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }} = _String{"{{ $member.Name }}"}
{{- end }}
)
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeTypeAssertions(w io.Writer) {
emitNodeTypeAssertions_typical(w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeMethodLookupByString(w io.Writer) {
doTemplate(`
func (n {{ .Type | TypeSymbol }}) LookupByString(key string) (ipld.Node, error) {
switch key {
{{- range $i, $member := .Type.Members }}
case "{{ $member.Name }}":
{{- if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "embedAll") }}
if n.tag != {{ $i }} {
return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(key)}
}
return &n.x{{ $i }}, nil
{{- else if (eq (dot.AdjCfg.UnionMemlayout dot.Type) "interface") }}
if _, ok := n.x.({{ $member | TypeSymbol }}); !ok {
return nil, ipld.ErrNotExists{ipld.PathSegmentOfString(key)}
}
return n.x, nil
{{- end}}
{{- end}}
default:
return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key}
}
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeMethodLookupByNode(w io.Writer) {
doTemplate(`
func (n {{ .Type | TypeSymbol }}) LookupByNode(key ipld.Node) (ipld.Node, error) {
ks, err := key.AsString()
if err != nil {
return nil, err
}
return n.LookupByString(ks)
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeMethodMapIterator(w io.Writer) {
// This is kind of a hilarious "iterator": it has to count all the way up to... 1.
doTemplate(`
func (n {{ .Type | TypeSymbol }}) MapIterator() ipld.MapIterator {
return &_{{ .Type | TypeSymbol }}__MapItr{n, false}
}
type _{{ .Type | TypeSymbol }}__MapItr struct {
n {{ .Type | TypeSymbol }}
done bool
}
func (itr *_{{ .Type | TypeSymbol }}__MapItr) Next() (k ipld.Node, v ipld.Node, _ error) {
if itr.done {
return nil, nil, ipld.ErrIteratorOverread{}
}
{{- if (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
switch itr.n.tag {
{{- range $i, $member := .Type.Members }}
case {{ $i }}:
return memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }}, &n.x{{ $i }}, nil
{{- end}}
{{- else if (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
switch itr.n.x.(type) {
{{- range $member := .Type.Members }}
case {{ $member | TypeSymbol }}:
return memberName__{{ dot.Type | TypeSymbol }}_{{ $member.Name }}, n.x, nil
{{- end}}
{{- end}}
default:
panic("unreachable")
}
itr.done = true
return
}
func (itr *_{{ .Type | TypeSymbol }}__MapItr) Done() bool {
return itr.done
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeMethodLength(w io.Writer) {
doTemplate(`
func ({{ .Type | TypeSymbol }}) Length() int {
return 1
}
`, w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodeMethodPrototype(w io.Writer) {
emitNodeMethodPrototype_typical(w, g.AdjCfg, g)
}
func (g unionGenerator) EmitNodePrototypeType(w io.Writer) {
emitNodePrototypeType_typical(w, g.AdjCfg, g)
}
func (g unionGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
return nil /* TODO */
}
package gengo
import (
"github.com/ipld/go-ipld-prime/schema"
"github.com/ipld/go-ipld-prime/schema/gen/go/mixins"
)
var _ TypeGenerator = &unionReprKeyedGenerator{}
func NewUnionReprKeyedGenerator(pkgName string, typ schema.TypeUnion, adjCfg *AdjunctCfg) TypeGenerator {
return unionReprKeyedGenerator{
unionGenerator{
adjCfg,
mixins.MapTraits{
pkgName,
string(typ.Name()),
adjCfg.TypeSymbol(typ),
},
pkgName,
typ,
},
}
}
type unionReprKeyedGenerator struct {
unionGenerator
}
func (g unionReprKeyedGenerator) GetRepresentationNodeGen() NodeGenerator {
return nil /* TODO */
}
......@@ -45,7 +45,12 @@ func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctC
case schema.TypeList:
EmitEntireType(NewListReprListGenerator(pkgName, t2, adjCfg), f)
case schema.TypeUnion:
panic("this ain't done yet :) you can't do the synthesis until the end, sadly")
switch t2.RepresentationStrategy().(type) {
case schema.UnionRepresentation_Keyed:
EmitEntireType(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
default:
panic("unrecognized union representation strategy")
}
default:
panic("add more type switches here :)")
}
......
......@@ -11,14 +11,10 @@ import (
"github.com/ipld/go-ipld-prime/schema"
)
func TestUnionPoke(t *testing.T) {
t.Skip("not implemented yet")
func TestUnionKeyed(t *testing.T) {
ts := schema.TypeSystem{}
ts.Init()
adjCfg := &AdjunctCfg{
maybeUsesPtr: map[schema.TypeName]bool{},
}
adjCfg := &AdjunctCfg{}
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnString("Strung"))
ts.Accumulate(schema.SpawnUnion("StrStr",
......@@ -32,9 +28,7 @@ func TestUnionPoke(t *testing.T) {
},
)))
prefix := "union-keyed"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
test := func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
np := getPrototypeByName("StrStr")
nrp := getPrototypeByName("StrStr.Repr")
var n schema.TypedNode
......@@ -60,5 +54,26 @@ func TestUnionPoke(t *testing.T) {
})
Wish(t, n, ShouldEqual, nr)
})
}
t.Skip("not yet finished") // mostly there -- all the templates work. can't compile until we finish all the builders.
t.Run("union-using-embed", func(t *testing.T) {
adjCfg.unionMemlayout = map[schema.TypeName]string{"StrStr": "embedAll"}
prefix := "union-keyed-using-embed"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
test(t, getPrototypeByName)
})
})
t.Run("union-using-interface", func(t *testing.T) {
adjCfg.unionMemlayout = map[schema.TypeName]string{"StrStr": "interface"}
prefix := "union-keyed-using-interface"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
test(t, getPrototypeByName)
})
})
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment