cue: implement embedded scalars
Mostly already implemented in evaluator.
Changes
- some adjustments in evaluator
- FieldSetDefined no longer necessary
as result of adjustments.
- print definitions alongside scalars
if requested in value mode.
Change-Id: Ie00dc2ef1dfe5a001c6133f0e7a27546687e2c9b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7728
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/comprehensions/incomplete.txtar b/cue/testdata/comprehensions/incomplete.txtar
index 1d4b4cf..5381c49 100644
--- a/cue/testdata/comprehensions/incomplete.txtar
+++ b/cue/testdata/comprehensions/incomplete.txtar
@@ -1,19 +1,26 @@
-- in.cue --
cond: bool
-src: {}
+src: {}
+top: _
a: [ if cond {} ]
b: [ for x in src.foo {} ]
+c: { for x in top {} }
-- out/eval --
(struct){
cond: (bool){ bool }
src: (struct){
}
+ top: (_){ _ }
a: (_|_){
// [incomplete] a: incomplete bool value 'bool'
}
b: (_|_){
// [incomplete] b: undefined field foo:
- // ./in.cue:4:19
+ // ./in.cue:5:19
+ }
+ c: (_|_){
+ // [incomplete] c: incomplete feed source value top (type _):
+ // ./in.cue:6:15
}
}
-- out/compile --
@@ -21,10 +28,14 @@
{
cond: bool
src: {}
+ top: _
a: [
if 〈0;cond〉 {},
]
b: [
for _, x in 〈0;src〉.foo {},
]
+ c: {
+ for _, x in 〈1;top〉 {}
+ }
}
diff --git a/cue/testdata/scalars/embed.txtar b/cue/testdata/scalars/embed.txtar
new file mode 100644
index 0000000..e3c9e28
--- /dev/null
+++ b/cue/testdata/scalars/embed.txtar
@@ -0,0 +1,125 @@
+-- in.cue --
+import "strings"
+
+a1: {
+ 2
+}
+a2: {
+ v: {
+ 3
+ #foo: a2.v + 1
+ }
+ w: v
+ x: v.#foo
+}
+a3: a1 + a2.v
+
+b3: {
+ [1, 2]
+ #foo: 1
+}
+b4: b3 + b3
+b5: b3[1]
+b6: b3[5]
+b7: b4[a1] // 1
+
+s1: {
+ "foo"
+ #bar: "bar"
+}
+s2: [ s1, { s1.#bar, #baz: 4 } ]
+s3: strings.Join(s2, "--")
+
+-- out/eval --
+Errors:
+b6: invalid list index 5 (out of bounds):
+ ./in.cue:22:8
+
+Result:
+(_|_){
+ // [eval]
+ a1: (int){ 2 }
+ a2: (struct){
+ v: (int){
+ 3
+ #foo: (int){ 4 }
+ }
+ w: (int){
+ 3
+ #foo: (int){ 4 }
+ }
+ x: (int){ 4 }
+ }
+ a3: (int){ 5 }
+ b3: (#list){
+ #foo: (int){ 1 }
+ 0: (int){ 1 }
+ 1: (int){ 2 }
+ }
+ b4: (#list){
+ 0: (int){ 1 }
+ 1: (int){ 2 }
+ 2: (int){ 1 }
+ 3: (int){ 2 }
+ }
+ b5: (int){ 2 }
+ b6: (_|_){
+ // [eval] b6: invalid list index 5 (out of bounds):
+ // ./in.cue:22:8
+ }
+ b7: (int){ 1 }
+ s1: (string){
+ "foo"
+ #bar: (string){ "bar" }
+ }
+ s2: (#list){
+ 0: (string){
+ "foo"
+ #bar: (string){ "bar" }
+ }
+ 1: (string){
+ "bar"
+ #baz: (int){ 4 }
+ }
+ }
+ s3: (string){ "foo--bar" }
+}
+-- out/compile --
+--- in.cue
+{
+ a1: {
+ 2
+ }
+ a2: {
+ v: {
+ 3
+ #foo: (〈2;a2〉.v + 1)
+ }
+ w: 〈0;v〉
+ x: 〈0;v〉.#foo
+ }
+ a3: (〈0;a1〉 + 〈0;a2〉.v)
+ b3: {
+ [
+ 1,
+ 2,
+ ]
+ #foo: 1
+ }
+ b4: (〈0;b3〉 + 〈0;b3〉)
+ b5: 〈0;b3〉[1]
+ b6: 〈0;b3〉[5]
+ b7: 〈0;b4〉[〈0;a1〉]
+ s1: {
+ "foo"
+ #bar: "bar"
+ }
+ s2: [
+ 〈0;s1〉,
+ {
+ 〈1;s1〉.#bar
+ #baz: 4
+ },
+ ]
+ s3: 〈import;strings〉.Join(〈0;s2〉, "--")
+}
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index 2e8806c..e5aeba4 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -228,10 +228,6 @@
// nodeContext to allow reusing the computations done so far.
Partial
- // FieldSetDefined indicates that the value and conjuncts of all fields have
- // been completed, but that the individual arcs are not yet evaluated.
- FieldSetDefined
-
// EvaluatingArcs indicates that the arcs of the Vertex are currently being
// evaluated. If this is encountered it indicates a structural cycle.
// Value does not have to be nil
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 5b52a18..3ae8892 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -387,7 +387,7 @@
v, complete := c.Evaluate(env, x)
- v, ok := c.getDefault(v)
+ v, ok := c.getDefault(v, true)
if !ok {
return v, false
}
@@ -406,13 +406,14 @@
return v, true
}
-func (c *OpContext) getDefault(v Value) (result Value, ok bool) {
+func (c *OpContext) getDefault(v Value, scalar bool) (result Value, ok bool) {
var d *Disjunction
switch x := v.(type) {
default:
return v, true
case *Vertex:
+ // TODO: return vertex if not disjunction.
switch t := x.Value.(type) {
case *Disjunction:
d = t
@@ -420,7 +421,13 @@
case *StructMarker, *ListMarker:
return v, true
+ case *Vertex:
+ return c.getDefault(t, scalar)
+
default:
+ if !scalar {
+ return v, true
+ }
return t, true
}
@@ -433,7 +440,7 @@
"unresolved disjunction %s (type %s)", c.Str(d), d.Kind())
return nil, false
}
- return c.getDefault(d.Values[0])
+ return c.getDefault(d.Values[0], scalar)
}
// Evaluate evaluates an expression within the given environment and indicates
@@ -473,12 +480,14 @@
func (c *OpContext) value(x Expr) (result Value) {
v := c.evalState(x, Partial)
- v, _ = c.getDefault(v)
+ v, _ = c.getDefault(v, true)
return v
}
-func (c *OpContext) eval(v Expr) (result Value) {
- return c.evalState(v, Partial)
+func (c *OpContext) eval(x Expr) (result Value) {
+ v := c.evalState(x, Partial)
+ return v
+
}
func (c *OpContext) evalState(v Expr, state VertexStatus) (result Value) {
@@ -626,36 +635,43 @@
return x.Source().Pos()
}
-func (c *OpContext) node(x Expr, state VertexStatus) *Vertex {
- v := c.evalState(x, state)
+func (c *OpContext) node(x Expr, scalar bool) *Vertex {
+ v := c.evalState(x, EvaluatingArcs)
- v, ok := c.getDefault(v)
+ v, ok := c.getDefault(v, scalar)
if !ok {
// Error already generated by getDefault.
return emptyNode
}
node, ok := v.(*Vertex)
- if !ok {
- if isError(v) {
- if v == nil {
- c.addErrf(IncompleteError, pos(x), "incomplete value %s", c.Str(x))
- return emptyNode
- }
- }
+ if ok {
+ v = node.Value
+ }
+ switch nv := v.(type) {
+ case nil:
+ c.addErrf(IncompleteError, pos(x), "incomplete value %s", c.Str(x))
+ return emptyNode
+
+ case *Bottom:
+ c.AddBottom(nv)
+ return emptyNode
+
+ case *StructMarker, *ListMarker:
+
+ default:
if v.Kind()&StructKind != 0 {
c.addErrf(IncompleteError, pos(x),
"incomplete feed source value %s (type %s)",
x.Source(), v.Kind())
- } else if b, ok := v.(*Bottom); ok {
- c.AddBottom(b)
- } else {
+ return emptyNode
+
+ } else if !ok {
c.addErrf(0, pos(x), // TODO(error): better message.
"invalid operand %s (found %s, want list or struct)",
x.Source(), v.Kind())
-
+ return emptyNode
}
- return emptyNode
}
return node.Default()
}
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index e0f7819..a2dd75f 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -585,7 +585,7 @@
}
func (x *SelectorExpr) resolve(c *OpContext) *Vertex {
- n := c.node(x.X, FieldSetDefined)
+ n := c.node(x.X, x.Sel.IsRegular())
return c.lookup(n, x.Src.Sel.Pos(), x.Sel)
}
@@ -608,7 +608,7 @@
func (x *IndexExpr) resolve(ctx *OpContext) *Vertex {
// TODO: support byte index.
- n := ctx.node(x.X, EvaluatingArcs)
+ n := ctx.node(x.X, true)
i := ctx.value(x.Index)
f := ctx.Label(i)
return ctx.lookup(n, x.Src.Index.Pos(), f)
@@ -1174,7 +1174,7 @@
}
func (x *ForClause) yield(c *OpContext, f YieldFunc) {
- n := c.node(x.Src, EvaluatingArcs)
+ n := c.node(x.Src, true)
for _, a := range n.Arcs {
c.Unify(c, a, Partial)
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 885a719..d323938 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -145,6 +145,7 @@
// can be the only value in a valid configuration. This means that an error
// may go undetected at this point, as long as it is caught later.
//
+// TODO: return *adt.Vertex
func (e *Evaluator) Evaluate(c *adt.OpContext, v *adt.Vertex) adt.Value {
var resultValue adt.Value
@@ -235,17 +236,14 @@
// TODO: Store if concrete and fully resolved.
}
- switch v.Value.(type) {
- case nil:
- // Error saved in result.
- return resultValue // incomplete
-
- case *adt.ListMarker, *adt.StructMarker:
- return v
-
- default:
- return v.Value
+ // TODO: Use this and ensure that each use of Evaluate handles
+ // struct numbers correctly. E.g. by using a function that
+ // gets the concrete value.
+ //
+ if v.Value == nil {
+ return resultValue
}
+ return v
}
// Unify implements adt.Unifier.
diff --git a/internal/core/export/export.go b/internal/core/export/export.go
index 5902187..880f49f 100644
--- a/internal/core/export/export.go
+++ b/internal/core/export/export.go
@@ -385,6 +385,11 @@
frame.fields[label] = entry
}
+func (e *exporter) addEmbed(x ast.Expr) {
+ frame := e.top()
+ frame.scope.Elts = append(frame.scope.Elts, x)
+}
+
func (e *exporter) pushFrame(conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) {
saved = e.stack
s = &ast.StructLit{}
diff --git a/internal/core/export/testdata/embedscalar.txtar b/internal/core/export/testdata/embedscalar.txtar
new file mode 100644
index 0000000..4302de6
--- /dev/null
+++ b/internal/core/export/testdata/embedscalar.txtar
@@ -0,0 +1,87 @@
+-- in.cue --
+#top: 4
+sub: {
+ #sub: 5
+ a: {
+ 3
+ #foo: 4
+ }
+ b: {
+ [1, 2]
+ #bar: 5
+ #baz: "foo"
+ }
+}
+-- out/definition --
+#top: 4
+sub: {
+ #sub: 5
+ a: {
+ 3
+ #foo: 4
+ }
+ b: {
+ [1, 2]
+ #bar: 5
+ #baz: "foo"
+ }
+}
+-- out/doc --
+[]
+[#top]
+[sub]
+[sub #sub]
+[sub a]
+[sub a #foo]
+[sub b]
+[sub b #bar]
+[sub b #baz]
+[sub b 0]
+[sub b 1]
+-- out/value --
+== Simplified
+{
+ sub: {
+ a: 3
+ b: [1, 2]
+ }
+}
+== Raw
+{
+ #top: 4
+ sub: {
+ #sub: 5
+ a: {
+ 3
+ #foo: 4
+ }
+ b: {
+ #bar: 5
+ #baz: "foo"
+ [1, 2]
+ }
+ }
+}
+== Final
+{
+ sub: {
+ a: 3
+ b: [1, 2]
+ }
+}
+== All
+{
+ #top: 4
+ sub: {
+ #sub: 5
+ a: {
+ 3
+ #foo: 4
+ }
+ b: {
+ #bar: 5
+ #baz: "foo"
+ [1, 2]
+ }
+ }
+}
diff --git a/internal/core/export/value.go b/internal/core/export/value.go
index 7bf8f11..7b5e99f 100644
--- a/internal/core/export/value.go
+++ b/internal/core/export/value.go
@@ -48,7 +48,11 @@
result = e.structComposite(n)
case *adt.ListMarker:
- result = e.listComposite(n)
+ if e.showArcs(n) {
+ result = e.structComposite(n)
+ } else {
+ result = e.listComposite(n)
+ }
case *adt.Bottom:
if !x.IsIncomplete() || len(n.Conjuncts) == 0 {
@@ -64,7 +68,11 @@
result = ast.NewBinExpr(token.AND, a...)
default:
- result = e.value(n.Value, n.Conjuncts...)
+ if e.showArcs(n) {
+ result = e.structComposite(n)
+ } else {
+ result = e.value(n.Value, n.Conjuncts...)
+ }
}
return result
}
@@ -297,6 +305,22 @@
return l
}
+func (e exporter) showArcs(v *adt.Vertex) bool {
+ p := e.cfg
+ if !p.ShowHidden && !p.ShowDefinitions {
+ return false
+ }
+ for _, a := range v.Arcs {
+ switch {
+ case a.Label.IsDef() && p.ShowDefinitions:
+ return true
+ case a.Label.IsHidden() && p.ShowHidden:
+ return true
+ }
+ }
+ return false
+}
+
func (e *exporter) structComposite(v *adt.Vertex) ast.Expr {
s, saved := e.pushFrame(v.Conjuncts)
e.top().upCount++
@@ -305,18 +329,32 @@
e.popFrame(saved)
}()
+ showRegular := false
+ switch x := v.Value.(type) {
+ case *adt.StructMarker:
+ showRegular = true
+ case *adt.ListMarker:
+ // As lists may be long, put them at the end.
+ defer e.addEmbed(e.listComposite(v))
+ default:
+ e.addEmbed(e.value(x))
+ }
+
p := e.cfg
for _, label := range VertexFeatures(v) {
- if label.IsDef() && !p.ShowDefinitions {
+ show := false
+ switch label.Typ() {
+ case adt.StringLabel:
+ show = showRegular
+ case adt.IntLabel:
continue
+ case adt.DefinitionLabel:
+ show = p.ShowDefinitions
+ case adt.HiddenLabel, adt.HiddenDefinitionLabel:
+ show = p.ShowHidden && label.PkgID(e.ctx) == e.pkgID
}
- if label.IsHidden() {
- if !p.ShowHidden {
- continue
- }
- if label.PkgID(e.ctx) != e.pkgID {
- continue
- }
+ if !show {
+ continue
}
f := &ast.Field{Label: e.stringLabel(label)}