Skip to content

Commit

Permalink
cue: implement embedded scalars
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mpvl committed Nov 23, 2020
1 parent 1c904cc commit 6c49cf0
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 50 deletions.
15 changes: 13 additions & 2 deletions cue/testdata/comprehensions/incomplete.txtar
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
-- 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 --
--- in.cue
{
cond: bool
src: {}
top: _
a: [
if 〈0;cond〉 {},
]
b: [
for _, x in 〈0;src〉.foo {},
]
c: {
for _, x in 〈1;top〉 {}
}
}
125 changes: 125 additions & 0 deletions cue/testdata/scalars/embed.txtar
Original file line number Diff line number Diff line change
@@ -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〉, "--")
}
4 changes: 0 additions & 4 deletions internal/core/adt/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,6 @@ const (
// 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
Expand Down
58 changes: 37 additions & 21 deletions internal/core/adt/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (c *OpContext) Concrete(env *Environment, x Expr, msg interface{}) (result

v, complete := c.Evaluate(env, x)

v, ok := c.getDefault(v)
v, ok := c.getDefault(v, true)
if !ok {
return v, false
}
Expand All @@ -406,21 +406,28 @@ func (c *OpContext) Concrete(env *Environment, x Expr, msg interface{}) (result
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

case *StructMarker, *ListMarker:
return v, true

case *Vertex:
return c.getDefault(t, scalar)

default:
if !scalar {
return v, true
}
return t, true
}

Expand All @@ -433,7 +440,7 @@ func (c *OpContext) getDefault(v Value) (result Value, ok bool) {
"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
Expand Down Expand Up @@ -473,12 +480,14 @@ func (c *OpContext) Evaluate(env *Environment, x Expr) (result Value, complete b
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) {
Expand Down Expand Up @@ -626,36 +635,43 @@ func pos(x Node) token.Pos {
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()
}
Expand Down
6 changes: 3 additions & 3 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ func (x *SelectorExpr) Source() ast.Node {
}

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)
}

Expand All @@ -608,7 +608,7 @@ func (x *IndexExpr) Source() ast.Node {

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)
Expand Down Expand Up @@ -1174,7 +1174,7 @@ func (x *ForClause) Source() ast.Node {
}

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)

Expand Down
18 changes: 8 additions & 10 deletions internal/core/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func (e *Evaluator) Eval(v *adt.Vertex) errors.Error {
// 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

Expand Down Expand Up @@ -235,17 +236,14 @@ func (e *Evaluator) Evaluate(c *adt.OpContext, v *adt.Vertex) adt.Value {
// 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.
Expand Down
5 changes: 5 additions & 0 deletions internal/core/export/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) {
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{}
Expand Down

0 comments on commit 6c49cf0

Please sign in to comment.