mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-30 11:31:37 -05:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
Reference in New Issue
Block a user