Skip to content

Commit

Permalink
internal/core/export: simplify dynamic labels
Browse files Browse the repository at this point in the history
This is now possible because of the passed down evaluator.

Note that most of it is actually left unimplemented as
revealing a new field may cause other fields to be
shadowed, which Sanatize doesn't seem to handle properly
yet.

Useful for Issue #867

Signed-off-by: Marcel van Lohuizen <mpvl@golang.org>
Change-Id: Ibd0f2f75f006cdd6c4e3e3eda428a0166a10b31c
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/541463
Reviewed-by: Paul Jolly <paul@myitcv.io>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUEcueckoo <cueckoo@cuelang.org>
  • Loading branch information
mpvl committed Jul 26, 2022
1 parent 9777511 commit 8edd739
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 25 deletions.
2 changes: 1 addition & 1 deletion cue/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,7 @@ func TestElem(t *testing.T) {
a: foo: b: [Bar=string]: { d: Bar }
`,
path: []string{"a", "foo", "b", ""},
want: "{\n\tc: string + string\n\td: string\n}",
want: "{\n\tc: \"foo\" + string\n\td: string\n}",
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions internal/core/adt/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ type Environment struct {
cache map[Expr]Value
}

func (e *Environment) up(count int32) *Environment {
for ; count > 0; count-- {
e = e.Up
}
return e
}

type ID int32

// evalCached is used to look up let expressions. Caching let expressions
Expand Down
16 changes: 3 additions & 13 deletions internal/core/adt/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,28 +291,18 @@ func spawn(env *Environment, node *Vertex) *Environment {
}

func (c *OpContext) Env(upCount int32) *Environment {
e := c.e
for ; upCount > 0; upCount-- {
e = e.Up
}
return e
return c.e.up(upCount)
}

func (c *OpContext) relNode(upCount int32) *Vertex {
e := c.e
for ; upCount > 0; upCount-- {
e = e.Up
}
e := c.e.up(upCount)
c.Unify(e.Vertex, Partial)
return e.Vertex
}

func (c *OpContext) relLabel(upCount int32) Feature {
// locate current label.
e := c.e
for ; upCount > 0; upCount-- {
e = e.Up
}
e := c.e.up(upCount)
return e.DynamicLabel
}

Expand Down
8 changes: 8 additions & 0 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,14 @@ func (x *DynamicReference) Source() ast.Node {
return x.Src
}

func (x *DynamicReference) EvaluateLabel(ctx *OpContext, env *Environment) Feature {
env = env.up(x.UpCount)
frame := ctx.PushState(env, x.Src)
v := ctx.value(x.Label)
ctx.PopState(frame)
return ctx.Label(x, v)
}

func (x *DynamicReference) resolve(ctx *OpContext, state VertexStatus) *Vertex {
e := ctx.Env(x.UpCount)
frame := ctx.PushState(e, x.Src)
Expand Down
39 changes: 31 additions & 8 deletions internal/core/export/adt.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ func (e *exporter) adt(env *adt.Environment, expr adt.Elem) ast.Expr {
// TODO: why does LabelReference not implement resolve?
case *adt.LabelReference:
// get potential label from Source. Otherwise use X.
v, ok := e.ctx.Evaluate(env, x)
f := e.frame(x.UpCount)
if ok && (adt.IsConcrete(v) || f.field == nil) {
return e.value(v)
}
if f.field == nil {
// This can happen when the LabelReference is evaluated outside of
// normal evaluation, that is, if a pattern constraint or
Expand Down Expand Up @@ -287,13 +291,16 @@ func (e *exporter) resolve(env *adt.Environment, r adt.Resolver) ast.Expr {
return ident

case *adt.DynamicReference:
// TODO(dynamic): ensure we correctly unshadow newly visible fields.
// TODO(unshadow): ensure we correctly unshadow newly visible fields.
// before uncommenting this.
//
// if v := x.EvaluateLabel(e.ctx, env); v != 0 {
// str := v.StringValue(e.ctx)
// if ast.IsValidIdent(str) {
// return ast.NewIdent(str)
// label := e.ctx.StringLabel(str)
// ident, ok := e.newIdentForField(x.Src, label, x.UpCount)
// if ok {
// return ident
// }
// }
// }

Expand Down Expand Up @@ -451,13 +458,29 @@ func (e *exporter) decl(env *adt.Environment, d adt.Decl) ast.Decl {

f := &ast.Field{}

key := e.expr(env, srcKey)
switch key.(type) {
case *ast.Interpolation, *ast.BasicLit:
v, _ := e.ctx.Evaluate(env, x.Key)

switch s, ok := v.(*adt.String); {
// TODO(unshadow): allow once unshadowing algorithm is fixed.
// case ok && ast.IsValidIdent(s.Str):
// label := e.ctx.StringLabel(s.Str)
// f.Label = ast.NewIdent(s.Str)
// e.setField(label, f)

case ok:
srcKey = s

fallthrough

default:
key = &ast.ParenExpr{X: key}
key := e.expr(env, srcKey)
switch key.(type) {
case *ast.Interpolation, *ast.BasicLit:
default:
key = &ast.ParenExpr{X: key}
}
f.Label = key.(ast.Label)
}
f.Label = key.(ast.Label)

alias := aliasFromLabel(x.Src)

Expand Down
4 changes: 2 additions & 2 deletions internal/core/export/testdata/main/adt.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ p1: {
}
}
d1: {
"foo\(bar)": int
"foobar": int
}
bar: "bar"
d2: {
C="foo\(bar)": {
C="foobar": {
name: "xx"
foo: C.name
}
Expand Down
2 changes: 1 addition & 1 deletion internal/core/export/testdata/main/alias.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fieldAlias: {
baz: 3
X_2="d-2": {
E=[D="cue"]: {
C="foo\(baz)": {
C="foo3": {
name: "xx"
foo: C.name
bar: X_2
Expand Down
111 changes: 111 additions & 0 deletions internal/core/export/testdata/main/shadow.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
In this file we test cases where a newly created dynamic field
ends up shadowing an existing field.

-- in.cue --
y: "outer"
a: {
key: "y"
X=(key): "inner"

refs: {
Z="y": 1
outer: y
mid: X // TODO(unshadow)
inner: Z // TODO(unshadow)

}
}
-- out/definition --
y: "outer"
a: {
key: "y"
X=(key): "inner"
refs: {
y: 1
outer: y
mid: X
inner: y
}
}
-- out/doc --
[]
[y]
[a]
[a key]
[a refs]
[a refs y]
[a refs outer]
[a refs mid]
[a refs inner]
[a y]
-- out/value --
== Simplified
{
y: "outer"
a: {
key: "y"
y: "inner"
refs: {
y: 1
outer: "outer"
mid: "inner"
inner: 1
}
}
}
== Raw
{
y: "outer"
a: {
key: "y"
y: "inner"
refs: {
y: 1
outer: "outer"
mid: "inner"
inner: 1
}
}
}
== Final
{
y: "outer"
a: {
key: "y"
y: "inner"
refs: {
y: 1
outer: "outer"
mid: "inner"
inner: 1
}
}
}
== All
{
y: "outer"
a: {
key: "y"
y: "inner"
refs: {
y: 1
outer: "outer"
mid: "inner"
inner: 1
}
}
}
== Eval
{
y: "outer"
a: {
key: "y"
y: "inner"
refs: {
y: 1
outer: "outer"
mid: "inner"
inner: 1
}
}
}

0 comments on commit 8edd739

Please sign in to comment.