NomDL Codegen: Partial support for recursive types

When computing if a Map/Set key contains another Map/Set/List we need
to ensure that we are not hitting a recursive type or we hit an i-loop.

Partial fix for #320
This commit is contained in:
Erik Arvidsson
2015-09-22 09:17:14 -04:00
parent 2769353a55
commit 9e7fa76506
4 changed files with 223 additions and 6 deletions
+12 -6
View File
@@ -556,22 +556,28 @@ func (gen *codeGen) writeEnum(t parse.TypeRef) {
// We use a go map as the def for Set and Map. These cannot have a key that is a
// Set, Map or a List because slices and maps are not comparable in go.
func (gen *codeGen) canUseDef(t parse.TypeRef) bool {
return gen.canUseDefRec(t, false)
visited := map[string]bool{}
return gen.canUseDefRec(t, visited, false)
}
func (gen *codeGen) canUseDefRec(t parse.TypeRef, deep bool) bool {
func (gen *codeGen) canUseDefRec(t parse.TypeRef, visited map[string]bool, inKey bool) bool {
t = gen.resolve(t)
switch t.Desc.Kind() {
case parse.ListKind:
return !deep && gen.canUseDefRec(t.Desc.(parse.CompoundDesc).ElemTypes[0], deep)
return !inKey && gen.canUseDefRec(t.Desc.(parse.CompoundDesc).ElemTypes[0], visited, inKey)
case parse.SetKind:
return !deep && gen.canUseDefRec(t.Desc.(parse.CompoundDesc).ElemTypes[0], true)
return !inKey && gen.canUseDefRec(t.Desc.(parse.CompoundDesc).ElemTypes[0], visited, true)
case parse.MapKind:
elemTypes := t.Desc.(parse.CompoundDesc).ElemTypes
return !deep && gen.canUseDefRec(elemTypes[0], true) && gen.canUseDefRec(elemTypes[1], false)
return !inKey && gen.canUseDefRec(elemTypes[0], visited, true) && gen.canUseDefRec(elemTypes[1], visited, false)
case parse.StructKind:
// Only structs can be recursive
if visited[gen.userName(t)] {
return true
}
visited[gen.userName(t)] = true
for _, f := range t.Desc.(parse.StructDesc).Fields {
if !gen.canUseDefRec(f.T, deep) {
if !gen.canUseDefRec(f.T, visited, inKey) {
return false
}
}
+181
View File
@@ -0,0 +1,181 @@
// This file was generated by nomdl/codegen.
package test
import (
"github.com/attic-labs/noms/ref"
"github.com/attic-labs/noms/types"
)
// Tree
type Tree struct {
m types.Map
}
func NewTree() Tree {
return Tree{types.NewMap(
types.NewString("$name"), types.NewString("Tree"),
types.NewString("Children"), types.NewList(),
)}
}
type TreeDef struct {
Children ListOfTreeDef
}
func (def TreeDef) New() Tree {
return Tree{
types.NewMap(
types.NewString("$name"), types.NewString("Tree"),
types.NewString("Children"), def.Children.New().NomsValue(),
)}
}
func (self Tree) Def() TreeDef {
return TreeDef{
ListOfTreeFromVal(self.m.Get(types.NewString("Children"))).Def(),
}
}
func TreeFromVal(val types.Value) Tree {
// TODO: Validate here
return Tree{val.(types.Map)}
}
func (self Tree) NomsValue() types.Value {
return self.m
}
func (self Tree) Equals(other Tree) bool {
return self.m.Equals(other.m)
}
func (self Tree) Ref() ref.Ref {
return self.m.Ref()
}
func (self Tree) Children() ListOfTree {
return ListOfTreeFromVal(self.m.Get(types.NewString("Children")))
}
func (self Tree) SetChildren(val ListOfTree) Tree {
return Tree{self.m.Set(types.NewString("Children"), val.NomsValue())}
}
// ListOfTree
type ListOfTree struct {
l types.List
}
func NewListOfTree() ListOfTree {
return ListOfTree{types.NewList()}
}
type ListOfTreeDef []TreeDef
func (def ListOfTreeDef) New() ListOfTree {
l := make([]types.Value, len(def))
for i, d := range def {
l[i] = d.New().NomsValue()
}
return ListOfTree{types.NewList(l...)}
}
func (self ListOfTree) Def() ListOfTreeDef {
l := make([]TreeDef, self.Len())
for i := uint64(0); i < self.Len(); i++ {
l[i] = TreeFromVal(self.l.Get(i)).Def()
}
return l
}
func ListOfTreeFromVal(val types.Value) ListOfTree {
// TODO: Validate here
return ListOfTree{val.(types.List)}
}
func (self ListOfTree) NomsValue() types.Value {
return self.l
}
func (l ListOfTree) Equals(p ListOfTree) bool {
return l.l.Equals(p.l)
}
func (l ListOfTree) Ref() ref.Ref {
return l.l.Ref()
}
func (l ListOfTree) Len() uint64 {
return l.l.Len()
}
func (l ListOfTree) Empty() bool {
return l.Len() == uint64(0)
}
func (self ListOfTree) Get(i uint64) Tree {
return TreeFromVal(self.l.Get(i))
}
func (l ListOfTree) Slice(idx uint64, end uint64) ListOfTree {
return ListOfTree{l.l.Slice(idx, end)}
}
func (self ListOfTree) Set(i uint64, val Tree) ListOfTree {
return ListOfTree{self.l.Set(i, val.NomsValue())}
}
func (l ListOfTree) Append(v ...Tree) ListOfTree {
return ListOfTree{l.l.Append(l.fromElemSlice(v)...)}
}
func (l ListOfTree) Insert(idx uint64, v ...Tree) ListOfTree {
return ListOfTree{l.l.Insert(idx, l.fromElemSlice(v)...)}
}
func (l ListOfTree) Remove(idx uint64, end uint64) ListOfTree {
return ListOfTree{l.l.Remove(idx, end)}
}
func (l ListOfTree) RemoveAt(idx uint64) ListOfTree {
return ListOfTree{(l.l.RemoveAt(idx))}
}
func (l ListOfTree) fromElemSlice(p []Tree) []types.Value {
r := make([]types.Value, len(p))
for i, v := range p {
r[i] = v.NomsValue()
}
return r
}
type ListOfTreeIterCallback func(v Tree) (stop bool)
func (l ListOfTree) Iter(cb ListOfTreeIterCallback) {
l.l.Iter(func(v types.Value) bool {
return cb(TreeFromVal(v))
})
}
type ListOfTreeIterAllCallback func(v Tree)
func (l ListOfTree) IterAll(cb ListOfTreeIterAllCallback) {
l.l.IterAll(func(v types.Value) {
cb(TreeFromVal(v))
})
}
type ListOfTreeFilterCallback func(v Tree) (keep bool)
func (l ListOfTree) Filter(cb ListOfTreeFilterCallback) ListOfTree {
nl := NewListOfTree()
l.IterAll(func(v Tree) {
if cb(v) {
nl = nl.Append(v)
}
})
return nl
}
+4
View File
@@ -0,0 +1,4 @@
struct Tree {
// TODO: Add parent pointer
children: List(Tree)
}
@@ -0,0 +1,26 @@
package test
import (
"testing"
"github.com/attic-labs/noms/Godeps/_workspace/src/github.com/stretchr/testify/assert"
)
func TestStructRecursiveChildren(t *testing.T) {
assert := assert.New(t)
root := TreeDef{
Children: []TreeDef{
TreeDef{},
TreeDef{
Children: []TreeDef{
TreeDef{},
},
},
},
}.New()
assert.Equal(uint64(2), root.Children().Len())
assert.Equal(uint64(0), root.Children().Get(0).Children().Len())
assert.Equal(uint64(1), root.Children().Get(1).Children().Len())
}