Skip to content

Commit

Permalink
cue: add ReferencePath
Browse files Browse the repository at this point in the history
Change-Id: I7f8024aca1200995368600010d3e024d7bd7c9a3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9421
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Apr 15, 2021
1 parent 3bdfa5d commit 38ad7c3
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 32 deletions.
33 changes: 33 additions & 0 deletions cue/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,36 @@ func (p pathError) kind() adt.FeatureType { return 0 }
func (p pathError) feature(r adt.Runtime) adt.Feature {
return adt.InvalidLabel
}

func valueToSel(v adt.Value) Selector {
switch x := adt.Unwrap(v).(type) {
case *adt.Num:
i, err := x.X.Int64()
if err != nil {
return Selector{&pathError{errors.Promote(err, "invalid number")}}
}
return Index(int(i))
case *adt.String:
return Str(x.Str)
default:
return Selector{pathError{errors.Newf(token.NoPos, "dynamic selector")}}
}
}

func featureToSel(f adt.Feature, r adt.Runtime) Selector {
switch f.Typ() {
case adt.StringLabel:
return Str(f.StringValue(r))
case adt.IntLabel:
return Index(f.Index())
case adt.DefinitionLabel:
return Def(f.IdentString(r))
case adt.HiddenLabel, adt.HiddenDefinitionLabel:
ident := f.IdentString(r)
pkg := f.PkgID(r)
return Hid(ident, pkg)
}
return Selector{pathError{
errors.Newf(token.NoPos, "unexpected feature type %v", f.Typ()),
}}
}
65 changes: 41 additions & 24 deletions cue/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1849,18 +1849,46 @@ func (v Value) instance() *Instance {
// inst.Lookup(path) resolves to the same value, or no path if this value is not
// a reference. If a reference contains index selection (foo[bar]), it will
// only return a reference if the index resolves to a concrete value.
//
// Deprecated: use ReferencePath
func (v Value) Reference() (inst *Instance, path []string) {
root, p := v.ReferencePath()
if !root.Exists() {
return nil, nil
}

inst = getImportFromNode(v.idx, root.v)
for _, sel := range p.Selectors() {
switch x := sel.sel.(type) {
case stringSelector:
path = append(path, string(x))
default:
path = append(path, sel.String())
}
}

return inst, path
}

// ReferencePath returns the value and path referred to by this value such that
// value.LookupPath(path) resolves to the same value, or no path if this value
// is not a reference.
func (v Value) ReferencePath() (root Value, p Path) {
// TODO: don't include references to hidden fields.
if v.v == nil || len(v.v.Conjuncts) != 1 {
return nil, nil
return Value{}, Path{}
}
ctx := v.ctx()
c := v.v.Conjuncts[0]

return reference(v.idx, ctx, c.Env, c.Expr())
x, path := reference(v.idx, ctx, c.Env, c.Expr())
if x == nil {
return Value{}, Path{}
}
return makeValue(v.idx, x), Path{path: path}
}

func reference(rt *runtime.Runtime, c *adt.OpContext, env *adt.Environment, r adt.Expr) (inst *Instance, path []string) {
func reference(rt *runtime.Runtime, c *adt.OpContext, env *adt.Environment, r adt.Expr) (inst *adt.Vertex, path []Selector) {
ctx := c
defer ctx.PopState(ctx.PushState(env, r.Source()))

Expand All @@ -1875,7 +1903,7 @@ func reference(rt *runtime.Runtime, c *adt.OpContext, env *adt.Environment, r ad
case *adt.FieldReference:
env := ctx.Env(x.UpCount)
inst, path = mkPath(rt, nil, env.Vertex)
path = append(path, x.Label.SelectorString(c))
path = appendSelector(path, featureToSel(x.Label, rt))

case *adt.LabelReference:
env := ctx.Env(x.UpCount)
Expand All @@ -1885,46 +1913,35 @@ func reference(rt *runtime.Runtime, c *adt.OpContext, env *adt.Environment, r ad
env := ctx.Env(x.UpCount)
inst, path = mkPath(rt, nil, env.Vertex)
v, _ := ctx.Evaluate(env, x.Label)
str := ctx.StringValue(v)
path = append(path, str)
path = appendSelector(path, valueToSel(v))

case *adt.ImportReference:
imp := x.ImportPath.StringValue(ctx)
inst = getImportFromPath(rt, imp)
inst, _ = rt.LoadImport(rt.LabelStr(x.ImportPath))

case *adt.SelectorExpr:
inst, path = reference(rt, c, env, x.X)
path = append(path, x.Sel.SelectorString(ctx))
path = appendSelector(path, featureToSel(x.Sel, rt))

case *adt.IndexExpr:
inst, path = reference(rt, c, env, x.X)
v, _ := ctx.Evaluate(env, x.Index)
str := ctx.StringValue(v)
path = append(path, str)
path = appendSelector(path, valueToSel(v))
}
if inst == nil {
return nil, nil
}
return inst, path
}

func mkPath(ctx *runtime.Runtime, a []string, v *adt.Vertex) (inst *Instance, path []string) {
func mkPath(r *runtime.Runtime, a []Selector, v *adt.Vertex) (root *adt.Vertex, path []Selector) {
if v.Parent == nil {
return getImportFromNode(ctx, v), a
return v, a
}
inst, path = mkPath(ctx, a, v.Parent)
path = append(path, v.Label.SelectorString(ctx))
return inst, path
root, path = mkPath(r, a, v.Parent)
path = appendSelector(path, featureToSel(v.Label, r))
return root, path
}

// // References reports all references used to evaluate this value. It does not
// // report references for sub fields if v is a struct.
// //
// // Deprecated: can be implemented in terms of Reference and Expr.
// func (v Value) References() [][]string {
// panic("deprecated")
// }

type options struct {
concrete bool // enforce that values are concrete
raw bool // show original values
Expand Down
48 changes: 40 additions & 8 deletions cue/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3104,7 +3104,7 @@ func TestTrimZeros(t *testing.T) {
}
}

func TestReference(t *testing.T) {
func TestReferencePath(t *testing.T) {
testCases := []struct {
input string
want string
Expand All @@ -3120,16 +3120,31 @@ func TestReference(t *testing.T) {
want: "a",
}, {
input: "v: w: x: a.b.c, a: b: c: 1",
want: "a b c",
want: "a.b.c",
}, {
input: "v: w: x: w.a.b.c, v: w: a: b: c: 1",
want: "v w a b c",
want: "v.w.a.b.c",
}, {
input: `v: w: x: w.a.b.c, v: w: a: b: c: 1, #D: 3, opt?: 3, "v\(#D)": 3, X: {a: 3}, X`,
want: "v w a b c",
want: "v.w.a.b.c",
}, {
input: `v: w: x: w.a[bb]["c"], v: w: a: b: c: 1, bb: "b"`,
want: "v w a b c",
input: `
v: w: x: w.a[bb]["c"]
v: w: a: b: c: 1
bb: "b"`,
want: "v.w.a.b.c",
}, {
input: `
X="\(y)": 1
v: w: x: X // TODO: Move up for crash
y: "foo"`,
want: "foo",
}, {
input: `
v: w: _
v: [X=string]: x: a[X]
a: w: 1`,
want: "a.w",
}, {
input: `v: {
for t in src {
Expand All @@ -3138,7 +3153,7 @@ func TestReference(t *testing.T) {
}
},
src: ["x", "y"]`,
want: "v w tx",
want: "v.w.tx",
}, {
input: `
v: w: x: a
Expand Down Expand Up @@ -3167,8 +3182,25 @@ func TestReference(t *testing.T) {
var r Runtime
inst, _ := r.Compile("in", tc.input) // getInstance(t, tc.input)
v := inst.Lookup("v", "w", "x")

root, path := v.ReferencePath()
if got := path.String(); got != tc.want {
t.Errorf("\n got %v;\nwant %v", got, tc.want)
}

if tc.want != "" {
want := "1"
if tc.alt != "" {
want = tc.alt
}
v := fmt.Sprint(root.LookupPath(path))
if v != want {
t.Errorf("path resolved to %s; want %s", v, want)
}
}

inst, a := v.Reference()
if got := strings.Join(a, " "); got != tc.want {
if got := strings.Join(a, "."); got != tc.want {
t.Errorf("\n got %v;\nwant %v", got, tc.want)
}

Expand Down

0 comments on commit 38ad7c3

Please sign in to comment.