Commit a9b5a196 authored by Eric Myhre's avatar Eric Myhre

More steps in union codegen.

Generates the type structure itself, and a marker interface.
Not much else yet.

Wired test harnesses and plugged out one example.

It's a bummer we don't really have a great way to poke part of it into
running until the whole thing satisfies the generator interface.
But it'll come soon enough.
parent 390d3feb
......@@ -35,29 +35,29 @@ func (unionGenerator) IsRepr() bool { return false } // hint used in some genera
// --- native content and specializations --->
func (g unionGenerator) EmitNativeType(w io.Writer) {
// We generate *two* types: a struct which acts as the union node,
// and also an interface which covers the members (and has an unexported marker function to make sure the set can't be extended).
//
// 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 "embedAll") -}}
{{- with (eq (.AdjCfg.UnionMemlayout .Type) "embedAll") }}
{{- range $member := .Type.Members }}
x_{{ $member.Name }} _{{ $member | TypeSymbol }}
{{- end}}
{{ with (eq .AdjCfg.UnionMemlayout "interface") -}}
{{- end}}
{{- with (eq (.AdjCfg.UnionMemlayout .Type) "interface") }}
x _{{ .Type | TypeSymbol }}__iface
{{- end}}
}
type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}
type _{{ .Type | TypeSymbol }}__iface interface {
_{{ .Type | TypeSymbol }}__member()
}
{{ with (eq .AdjCfg.UnionMemlayout "interface") -}}
// ... is there any utility to making an interface type?
// internally: no.
// for export and use: unclear.
// i don't think we need to kowtow to burntsushi's sumtype checker. we could (and it would be pleasing to do so); but we can make our own just as well.
// how would you use it? would there be a method for unboxing the structptr into the interface type? and vice versa (which would incur an alloc)?
// if assignNode was going to work, ... actually it could just take the member type "concretely", they already implement Node, and we could just figure it out. That'd be fine.
// is there... any reason a programmer would prefer to use the interface, though?
// if they wanted to make their own typeswitch, maybe?
// is that going to be more efficient that doing a switch using an enum we generate, and then calling a function that does a cast explicitly? probably. heck.
// do we need to have an *exported* interface for this to work, though? I think not. so that might be a solid choice.
{{- range $member := .Type.Members }}
func (_{{ $member | TypeSymbol }}) _{{ .dot.Type | TypeSymbol }}__member() {}
{{- end}}
`, w, g.AdjCfg, g)
}
......@@ -44,6 +44,8 @@ func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctC
EmitEntireType(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
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")
default:
panic("add more type switches here :)")
}
......
package gengo
import (
"testing"
. "github.com/warpfork/go-wish"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/fluent"
"github.com/ipld/go-ipld-prime/must"
"github.com/ipld/go-ipld-prime/schema"
)
func TestUnionPoke(t *testing.T) {
t.Skip("not implemented yet")
ts := schema.TypeSystem{}
ts.Init()
adjCfg := &AdjunctCfg{
maybeUsesPtr: map[schema.TypeName]bool{},
}
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnString("Strung"))
ts.Accumulate(schema.SpawnUnion("StrStr",
[]schema.Type{
ts.TypeByName("String"),
ts.TypeByName("Strung"),
},
schema.SpawnUnionRepresentationKeyed(map[string]schema.Type{
"a": ts.TypeByName("String"),
"b": ts.TypeByName("Strung"),
},
)))
prefix := "union-keyed"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
np := getPrototypeByName("StrStr")
nrp := getPrototypeByName("StrStr.Repr")
var n schema.TypedNode
t.Run("typed-create", func(t *testing.T) {
n = fluent.MustBuildMap(np, 1, func(na fluent.MapAssembler) {
na.AssembleEntry("Strung").AssignString("whee")
}).(schema.TypedNode)
t.Run("typed-read", func(t *testing.T) {
Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map)
Wish(t, n.Length(), ShouldEqual, 1)
Wish(t, must.String(must.Node(n.LookupByString("Strung"))), ShouldEqual, "whee")
})
t.Run("repr-read", func(t *testing.T) {
nr := n.Representation()
Require(t, nr.ReprKind(), ShouldEqual, ipld.ReprKind_Map)
Wish(t, nr.Length(), ShouldEqual, 1)
Wish(t, must.String(must.Node(n.LookupByString("b"))), ShouldEqual, "whee")
})
})
t.Run("repr-create", func(t *testing.T) {
nr := fluent.MustBuildMap(nrp, 2, func(na fluent.MapAssembler) {
na.AssembleEntry("b").AssignString("whee")
})
Wish(t, n, ShouldEqual, nr)
})
})
}
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