Skip to content

Commit

Permalink
internal/core/eval: track fieldSets for data values
Browse files Browse the repository at this point in the history
This is, unfortunately, necessary because of
acceptor.isClosed. This tracking can be removed
once we get rid of that value.

Fixes #530

Change-Id: I34fe6d8af0667200595e000c02ca5898a4ab9f8b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7745
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
  • Loading branch information
mpvl committed Nov 25, 2020
1 parent 9f1fec6 commit acc35f1
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 22 deletions.
6 changes: 5 additions & 1 deletion internal/core/adt/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,11 @@ func appendPath(a []Feature, v *Vertex) []Feature {
return a
}
a = appendPath(a, v.Parent)
return append(a, v.Label)
if v.Label != 0 {
// A Label may be 0 for programmatically inserted nodes.
a = append(a, v.Label)
}
return a
}

func (v *Vertex) appendListArcs(arcs []*Vertex) (err *Bottom) {
Expand Down
16 changes: 4 additions & 12 deletions internal/core/convert/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,7 @@ func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {

case reflect.Struct:
obj := &adt.StructLit{Src: src}
v := &adt.Vertex{}
v.AddConjunct(adt.MakeRootConjunct(nil, obj))
v := &adt.Vertex{Structs: []*adt.StructLit{obj}}
v.SetValue(ctx, adt.Finalized, &adt.StructMarker{})

t := value.Type()
Expand Down Expand Up @@ -440,10 +439,7 @@ func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {
if sf.Anonymous && name == "" {
arc, ok := sub.(*adt.Vertex)
if ok {
for _, a := range arc.Arcs {
obj.Decls = append(obj.Decls, &adt.Field{Label: a.Label, Value: a.Value})
v.Arcs = append(v.Arcs, a)
}
v.Arcs = append(v.Arcs, arc.Arcs...)
}
continue
}
Expand All @@ -455,6 +451,7 @@ func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {
arc.Label = f
} else {
arc = &adt.Vertex{Label: f, Value: sub}
arc.UpdateStatus(adt.Finalized)
arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
}
v.Arcs = append(v.Arcs, arc)
Expand All @@ -463,9 +460,7 @@ func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {
return v

case reflect.Map:
obj := &adt.StructLit{Src: src}
v := &adt.Vertex{Value: &adt.StructMarker{}}
v.AddConjunct(adt.MakeRootConjunct(nil, obj))
v.SetValue(ctx, adt.Finalized, &adt.StructMarker{})

t := value.Type()
Expand Down Expand Up @@ -497,15 +492,12 @@ func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value {

s := fmt.Sprint(k)
f := ctx.StringLabel(s)
obj.Decls = append(obj.Decls, &adt.Field{
Label: f,
Value: sub,
})
arc, ok := sub.(*adt.Vertex)
if ok {
arc.Label = f
} else {
arc = &adt.Vertex{Label: f, Value: sub}
arc.UpdateStatus(adt.Finalized)
arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
}
v.Arcs = append(v.Arcs, arc)
Expand Down
26 changes: 21 additions & 5 deletions internal/core/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -1393,21 +1393,37 @@ func (n *nodeContext) addValueConjunct(env *adt.Environment, v adt.Value, id adt
return

case *adt.StructMarker:
// TODO: this would not be necessary if acceptor.isClose were
// not used. See comment at acceptor.
opt := fieldSet{env: env, id: id}

// Keep ordering of Go struct for topological sort.
n.node.Structs = append(n.node.Structs, x.Structs...)
for _, a := range x.Arcs {
c := adt.MakeConjunct(nil, a, id)
c = updateCyclic(c, cyclic, nil, nil)
n.insertField(a.Label, c)
opt.MarkField(ctx, a.Label)
}

closedInfo(n.node).insertFieldSet(id, &opt)

default:
n.addValueConjunct(env, v, id)

// TODO: this would not be necessary if acceptor.isClose were
// not used. See comment at acceptor.
opt := fieldSet{env: env, id: id}

for _, a := range x.Arcs {
// TODO(errors): report error when this is a regular field.
n.insertField(a.Label, adt.MakeConjunct(nil, a, id))
// sub, _ := n.node.GetArc(a.Label)
// sub.Add(a)
c := adt.MakeConjunct(nil, a, id)
c = updateCyclic(c, cyclic, nil, nil)
n.insertField(a.Label, c)
opt.MarkField(ctx, a.Label)
}

closedInfo(n.node).insertFieldSet(id, &opt)
}

return
Expand All @@ -1434,7 +1450,7 @@ func (n *nodeContext) addValueConjunct(env *adt.Environment, v adt.Value, id adt

case *adt.Top:
n.hasTop = true
// TODO: Is this correct. Needed for elipsis, but not sure for others.
// TODO: Is this correct? Needed for elipsis, but not sure for others.
// n.optionals = append(n.optionals, fieldSet{env: env, id: id, isOpen: true})
if a, _ := n.node.Closed.(*acceptor); a != nil {
// Embedding top indicates that now all possible values are allowed
Expand Down Expand Up @@ -1604,7 +1620,7 @@ func (n *nodeContext) addStruct(
for _, d := range s.Decls {
switch x := d.(type) {
case *adt.Field:
opt.MarkField(ctx, x)
opt.MarkField(ctx, x.Label)
// handle in next iteration.

case *adt.OptionalField:
Expand Down
6 changes: 3 additions & 3 deletions internal/core/eval/optionals.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ func (o *fieldSet) fieldIndex(f adt.Feature) int {
return -1
}

func (o *fieldSet) MarkField(c *adt.OpContext, x *adt.Field) {
if o.fieldIndex(x.Label) < 0 {
o.fields = append(o.fields, field{label: x.Label})
func (o *fieldSet) MarkField(c *adt.OpContext, f adt.Feature) {
if o.fieldIndex(f) < 0 {
o.fields = append(o.fields, field{label: f})
}
}

Expand Down
142 changes: 142 additions & 0 deletions internal/core/eval/vertex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package eval_test

import (
"testing"

"cuelang.org/go/cue"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/convert"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/eval"
)

// TestVertex tests the use of programmatically generated Vertex values that
// may have different properties than the one generated.
func TestVertex(t *testing.T) {
r := cue.NewRuntime()
ctx := eval.NewContext(r, nil)

goValue := func(x interface{}) *adt.Vertex {
v := convert.GoValueToValue(ctx, x, true)
return v.(*adt.Vertex)
}

goType := func(x interface{}) *adt.Vertex {
v, err := convert.GoTypeToExpr(ctx, x)
if err != nil {
t.Fatal(err)
}
n := &adt.Vertex{}
n.AddConjunct(adt.MakeRootConjunct(nil, v))
n.Finalize(ctx)
return n
}

schema := func(field, config string) *adt.Vertex {
v := build(t, ctx, config)
f := adt.MakeIdentLabel(r, field, "")
return v.Lookup(f)
}

testCases := []struct {
name string
a, b *adt.Vertex
want string
verbose bool
}{{

// Issue #530
a: schema("Steps", `
Steps: [...#Step]
#Step: Args: _
`),
b: goValue([]*struct{ Args interface{} }{{
Args: map[string]interface{}{
"Message": "Hello, world!",
},
}}),
want: `[{Args:{Message:"Hello, world!"}}]`,
}, {

name: "list of values",
a: goValue([]int{1, 2, 3}),
b: goType([]int{}),
want: `[1,2,3]`,
}, {
name: "list of list of values",
a: goValue([][]int{{1, 2, 3}}),
b: goType([][]int{}),
want: `[[1,2,3]]`,
}, {
a: schema("Steps", `
Steps: [...#Step]
#Step: Args: _
`),
b: schema("a", `
a: [{Args: { Message: "hello" }}]
`),
want: `[{Args:{Message:"hello"}}]`,
}, {

// Issue #530
a: schema("Steps", `
Steps: [...#Step]
#Step: Args: _
`),
b: goValue([]*struct{ Args interface{} }{{
Args: map[string]interface{}{
"Message": "Hello, world!",
},
}}),
want: `[{Args:{Message:"Hello, world!"}}]`,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {

n := &adt.Vertex{}
n.AddConjunct(adt.MakeRootConjunct(nil, tc.a))
n.AddConjunct(adt.MakeRootConjunct(nil, tc.b))
n.Finalize(ctx)

got := debug.NodeString(r, n, &debug.Config{Compact: !tc.verbose})
if got != tc.want {
t.Errorf("got:\n%s\nwant:\n%s", got, tc.want)
}
})
}
}

func build(t *testing.T, ctx *adt.OpContext, schema string) *adt.Vertex {
f, err := parser.ParseFile("test", schema)
if err != nil {
t.Fatal(err)
}

v, err := compile.Files(nil, ctx, "test", f)
if err != nil {
t.Fatal(err)
}

v.Finalize(ctx)

return v
}
2 changes: 1 addition & 1 deletion internal/core/export/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestGenerated(t *testing.T) {
}
return convert.GoValueToValue(ctx, in, false), nil
},
out: `{Terminals: [{Description: "Desc", Name: "Name"}]}`,
out: `{Terminals: [{Name: "Name", Description: "Desc"}]}`,
}, {
in: func(ctx *adt.OpContext) (adt.Expr, error) {
in := &C{
Expand Down

0 comments on commit acc35f1

Please sign in to comment.