cue: implementation of marked defaults
Change-Id: I7b0ed2b1e372a71410cbd05253ca819f08509a2a
diff --git a/cmd/cue/cmd/testdata/trim/trim.cue b/cmd/cue/cmd/testdata/trim/trim.cue
index db0ca2e..362e3c5 100644
--- a/cmd/cue/cmd/testdata/trim/trim.cue
+++ b/cmd/cue/cmd/testdata/trim/trim.cue
@@ -14,7 +14,7 @@
struct: {a: 3.0}
- sList: [{a: 8, b: string}, {a: 9, b: "foo" | string}]
+ sList: [{a: 8, b: string}, {a: 9, b: *"foo" | string}]
rList: [{a: "a"}]
rcList: [{a: "a", c: b}]
diff --git a/cmd/cue/cmd/testdata/trim/trim.out b/cmd/cue/cmd/testdata/trim/trim.out
index 70677ac..862b8a4 100644
--- a/cmd/cue/cmd/testdata/trim/trim.out
+++ b/cmd/cue/cmd/testdata/trim/trim.out
@@ -14,7 +14,7 @@
struct: {a: 3.0}
- sList: [{a: 8, b: string}, {a: 9, b: "foo" | string}]
+ sList: [{a: 8, b: string}, {a: 9, b: *"foo" | string}]
rList: [{a: "a"}]
rcList: [{a: "a", c: b}]
diff --git a/cue/ast.go b/cue/ast.go
index 43a3616..013b119 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -457,6 +457,9 @@
value = call
case *ast.UnaryExpr:
+ if n.Op == token.MUL {
+ return v.error(n, "preference mark not allowed at this position")
+ }
value = &unaryExpr{
newExpr(n),
tokenMap[n.Op],
@@ -466,7 +469,10 @@
case *ast.BinaryExpr:
switch n.Op {
case token.DISJUNCTION:
- value = makeDisjunction(v.ctx(), n, v.walk(n.X), v.walk(n.Y))
+ d := &disjunction{baseValue: newExpr(n)}
+ v.addDisjunctionElem(d, n.X, false)
+ v.addDisjunctionElem(d, n.Y, false)
+ value = d
case token.RANGE:
value = &rangeLit{
newExpr(n),
@@ -493,6 +499,23 @@
return value
}
+func (v *astVisitor) addDisjunctionElem(d *disjunction, n ast.Node, mark bool) {
+ switch x := n.(type) {
+ case *ast.BinaryExpr:
+ if x.Op == token.DISJUNCTION {
+ v.addDisjunctionElem(d, x.X, mark)
+ v.addDisjunctionElem(d, x.Y, mark)
+ return
+ }
+ case *ast.UnaryExpr:
+ if x.Op == token.MUL {
+ mark = true
+ n = x.X
+ }
+ }
+ d.values = append(d.values, dValue{v.walk(n), mark})
+}
+
func wrapClauses(v *astVisitor, y yielder, clauses []ast.Clause) yielder {
for _, c := range clauses {
if n, ok := c.(*ast.ForClause); ok {
diff --git a/cue/ast_test.go b/cue/ast_test.go
index f9bdd8e..8ae1aff 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -137,12 +137,13 @@
}, {
in: `
a: 5 | "a" | true
+ aa: 5 | *"a" | true
b c: {
cc: { ccc: 3 }
}
d: true
`,
- out: "<0>{a: (5 | \"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
+ out: "<0>{a: (5 | \"a\" | true), aa: (5 | *\"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
}, {
in: `
a a: { b: a } // referencing ancestor nodes is legal.
@@ -232,6 +233,13 @@
d: (2+3)..(4+5)
`,
out: `<0>{a: (1..2), b: ((1..2)..3), c: ("a".."b"), d: ((2 + 3)..(4 + 5))}`,
+ }, {
+ in: `
+ a: *1,
+ b: **1 | 2
+ `,
+ out: `<0>{a: _|_(preference mark not allowed at this position), ` +
+ `b: (*_|_(preference mark not allowed at this position) | 2)}`,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
@@ -276,7 +284,7 @@
a: 8000 | 7080
a: 7080 | int
}`,
- out: `<0>{a: _|_((8000! | 7080! | 7080):ambiguous disjunction)}`,
+ out: `<0>{a: _|_((8000 | 7080):more than one element remaining (8000 and 7080))}`,
rw: evalFull,
}}
for _, tc := range testCases {
diff --git a/cue/binop.go b/cue/binop.go
index 30c99bb..dec382e 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -31,12 +31,6 @@
return baseValue{&computedSource{pos, op, a, b}}
}
-type binFunc func(ctx *context, src source, op op, left, right evaluated) evaluated
-
-func binSwap(ctx *context, src source, op op, x, y evaluated) evaluated {
- return binOp(ctx, src, op, y, x)
-}
-
func unify(ctx *context, src source, left, right evaluated) evaluated {
return binOp(ctx, src, opUnify, left, right)
}
@@ -65,17 +59,6 @@
// op == opUnify
- // Evaluated and recompose disjunctions.
-
- if dl, ok := left.(*disjunction); ok {
- if dr, ok := right.(*disjunction); ok {
- return dl.cross(ctx, src, op, dr)
- }
- return dl.expand(ctx, src, op, right, binOp)
- } else if dr, ok := right.(*disjunction); ok {
- return dr.expand(ctx, src, op, left, binSwap)
- }
-
// TODO: unify type masks.
if left == right {
return left
@@ -87,6 +70,12 @@
return left
}
+ if dl, ok := left.(*disjunction); ok {
+ return distribute(ctx, src, dl, right)
+ } else if dr, ok := right.(*disjunction); ok {
+ return distribute(ctx, src, dr, left)
+ }
+
// TODO: value may be incomplete if there is a cycle. Instead of an error
// schedule an assert and return the atomic value, if applicable.
v := left.binOp(ctx, src, op, right)
@@ -101,48 +90,35 @@
return v
}
-func (x *disjunction) expand(ctx *context, src source, op op, y evaluated, fn binFunc) evaluated {
- // disjunction & single value
- dn := disjunction{src.base(), make([]dValue, 0, len(x.values))}
- changed := false
- for _, v := range x.values {
- e := fn(ctx, src, op, v.val.(evaluated), y)
- changed = changed || e != v.val
- dn.add(ctx, e, v.ambiguous)
- }
- if !changed {
- return x
- }
- return dn.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated)
+type mVal struct {
+ val evaluated
+ mark bool
}
-func (x *disjunction) cross(ctx *context, src source, op op, y *disjunction) evaluated {
- vr := []dValue{}
- idx := make([]int, 0, len(y.values))
- for _, va := range x.values {
- for j, vb := range y.values {
- e := binOp(ctx, src, op, va.val.(evaluated), vb.val.(evaluated))
- if isBottom(e) {
- continue
- }
+// distribute distributes a value over the element of a disjunction in a
+// unification operation. If allowCycle is true, references that resolve
+// to a cycle are dropped.
+func distribute(ctx *context, src source, x *disjunction, y evaluated) evaluated {
+ return dist(ctx, src, x, mVal{y, false}).val
+}
- // TODO: filter using subsumption.
+func dist(ctx *context, src source, dx *disjunction, y mVal) mVal {
+ dn := &disjunction{src.base(), make([]dValue, 0, len(dx.values))}
+ for _, dv := range dx.values {
+ x := mVal{dv.val.evalPartial(ctx), dv.marked}
+ src := binSrc(src.Pos(), opUnify, x.val, y.val)
- // Mark crossing values as ambiguous.
- ambiguous := va.ambiguous || vb.ambiguous
- for xi, x := range idx {
- if x > j {
- vr[xi].ambiguous = true
- ambiguous = true
- }
- }
- vr = append(vr, dValue{e, ambiguous})
- idx = append(idx, j)
+ var v mVal
+ if dy, ok := y.val.(*disjunction); ok {
+ v = dist(ctx, src, dy, x)
+ } else if ddv, ok := dv.val.(*disjunction); ok {
+ v = dist(ctx, src, ddv, y)
+ } else {
+ v = mVal{binOp(ctx, src, opUnify, x.val, y.val), x.mark || y.mark}
}
+ dn.add(ctx, v.val, v.mark)
}
-
- d := &disjunction{baseValue: src.base(), values: vr}
- return d.simplify(ctx, binSrc(src.Pos(), op, x, y)).(evaluated)
+ return dn.normalize(ctx, src)
}
func (x *disjunction) binOp(ctx *context, src source, op op, other evaluated) evaluated {
diff --git a/cue/debug.go b/cue/debug.go
index 91156eb..c51cc1d 100644
--- a/cue/debug.go
+++ b/cue/debug.go
@@ -210,10 +210,10 @@
if i != 0 {
writef(" | ")
}
- p.debugStr(v.val)
- if v.ambiguous {
- writef("!")
+ if v.marked {
+ writef("*")
}
+ p.debugStr(v.val)
}
write(")")
case *lambdaExpr:
diff --git a/cue/eval.go b/cue/eval.go
index 02c1117..e3ca92d 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -313,33 +313,51 @@
for _, v := range x.values {
n := v.val.evalPartial(ctx)
changed = changed || n != v.val
- dn.add(ctx, n, v.ambiguous)
+ dn.add(ctx, n, v.marked)
}
// TODO: move to evaluator
if !changed {
return x
}
- return dn.simplify(ctx, x).(evaluated)
+ return dn.normalize(ctx, x).val
}
func (x *disjunction) manifest(ctx *context) (result evaluated) {
- switch len(x.values) {
- case 0:
- return x.simplify(ctx, x).(evaluated) // force error
- case 1:
- return x.values[0].val.(evaluated)
- default:
- for _, d := range x.values {
- if validate(ctx, d.val) != nil {
- continue
- }
- if d.ambiguous {
- // better error
- return ctx.mkErr(x, "ambiguous disjunction")
- }
- return d.val.(evaluated)
+ var err, marked, unmarked1, unmarked2 evaluated
+ for _, d := range x.values {
+ // Because of the lazy evaluation strategy, we may still have
+ // latent unification.
+ if err := validate(ctx, d.val); err != nil {
+ continue
}
- return ctx.mkErr(x, "empty disjunction after evaluation")
+ switch {
+ case d.marked:
+ if marked != nil {
+ return ctx.mkErr(x, "more than one default remaining (%v and %v)", debugStr(ctx, marked), debugStr(ctx, d.val))
+ }
+ marked = d.val.(evaluated)
+ case unmarked1 == nil:
+ unmarked1 = d.val.(evaluated)
+ default:
+ unmarked2 = d.val.(evaluated)
+ }
+ }
+ switch {
+ case marked != nil:
+ return marked
+
+ case unmarked2 != nil:
+ return ctx.mkErr(x, "more than one element remaining (%v and %v)",
+ debugStr(ctx, unmarked1), debugStr(ctx, unmarked2))
+
+ case unmarked1 != nil:
+ return unmarked1
+
+ case err != nil:
+ return err
+
+ default:
+ return ctx.mkErr(x, "empty disjunction")
}
}
diff --git a/cue/export.go b/cue/export.go
index 6774be7..15c0b04 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -103,20 +103,22 @@
if len(x.values) == 1 {
return p.expr(x.values[0].val)
}
+ expr := func(v dValue) ast.Expr {
+ e := p.expr(v.val)
+ if v.marked {
+ e = &ast.UnaryExpr{Op: token.MUL, X: e}
+ }
+ return e
+ }
bin := &ast.BinaryExpr{
- X: p.expr(x.values[0].val),
+ X: expr(x.values[0]),
Op: token.DISJUNCTION,
- Y: p.expr(x.values[1].val),
+ Y: expr(x.values[1]),
}
for _, v := range x.values[2:] {
- bin = &ast.BinaryExpr{X: bin, Op: token.DISJUNCTION, Y: p.expr(v.val)}
+ bin = &ast.BinaryExpr{X: bin, Op: token.DISJUNCTION, Y: expr(v)}
}
return bin
- // case *lambdaExpr:
-
- // p.debugStr(x.params.arcs)
- // write(")->")
- // p.debugStr(x.value)
case *structLit:
obj := &ast.StructLit{}
diff --git a/cue/export_test.go b/cue/export_test.go
index 1b8701a..f5dd274 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -113,10 +113,10 @@
}`),
}, {
raw: true,
- in: `{ a: "foo" | "bar" | string, b: a[2:3] }`,
+ in: `{ a: *"foo" | *"bar" | *string | int, b: a[2:3] }`,
out: unindent(`
{
- a: "foo" | "bar" | string
+ a: *"foo" | *"bar" | *string | int
b: a[2:3]
}`),
}, {
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index f6ed64b..13870ec 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -1109,7 +1109,7 @@
}
switch p.tok {
- case token.ADD, token.SUB, token.NOT:
+ case token.ADD, token.SUB, token.NOT, token.MUL:
pos, op := p.pos, p.tok
c := p.openComments()
p.next()
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index b8108fe..aab3081 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -269,19 +269,39 @@
in: `
o1: 1 | 2 | 3
o2: (1 | 2 | 3) & 1
- o3: 2 & (1 | 2 | 3)
- o4: (1 | 2 | 3) & (1 | 2 | 3)
- o5: (1 | 2 | 3) & (3 | 2 | 1)
+ o3: 2 & (1 | *2 | 3)
+ o4: (1 | *2 | 3) & (1 | 2 | *3)
+ o5: (1 | *2 | 3) & (3 | *2 | 1)
o6: (1 | 2 | 3) & (3 | 1 | 2)
o7: (1 | 2 | 3) & (2 | 3)
o8: (1 | 2 | 3) & (3 | 2)
o9: (2 | 3) & (1 | 2 | 3)
- o10: (3 | 2) & (1 | 2 | 3)
+ o10: (3 | 2) & (1 | *2 | 3)
+
+ m1: (*1 | (*2 | 3)) & 2..3
+ m2: (*1 | (*2 | 3)) & (2 | 3)
+ m3: (*1 | *(*2 | 3)) & (2 | 3)
+ m4: (2 | 3) & (*2 | 3)
+ m5: (*2 | 3) & (2 | 3)
+
+ // (*2 | 3) & (2 | 3)
+ // (2 | 3) & (*2 | 3)
+ // 2&(*2 | 3) | 3&(*2 | 3)
+ // (*1 | (*2 | 3)) & (2 | 3)
+ // *1& (2 | 3) | (*2 | 3)&(2 | 3)
+ // *2&(2 | 3) | 3&(2 | 3)
+
+ // (2 | 3)&(*1 | (*2 | 3))
+ // 2&(*1 | (*2 | 3)) | 3&(*1 | (*2 | 3))
+ // *1&2 | (*2 | 3)&2 | *1&3 | (*2 | 3)&3
+ // (*2 | 3)&2 | (*2 | 3)&3
+ // *2 | 3
+
// All errors are treated the same as per the unification model.
i1: [1, 2][3] | "c"
`,
- out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | 2 | 3), o5: (1! | 2! | 3!), o6: (1! | 2! | 3!), o7: (2 | 3), o8: (2! | 3!), o9: (2 | 3), o10: (3! | 2!), i1: "c"}`,
+ out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | *2 | *3), o5: (1 | *2 | 3), o6: (1 | 2 | 3), o7: (2 | 3), o8: (2 | 3), o9: (2 | 3), o10: (3 | *2), m1: (*2 | 3), m2: (*2 | 3), m3: (*2 | 3), m4: (*2 | 3), m5: (*2 | 3), i1: "c"}`,
}, {
desc: "types",
in: `
@@ -365,8 +385,8 @@
testCases := []testCase{{
desc: "pick first",
in: `
- a: 5 | "a" | true
- b c: {
+ a: *5 | "a" | true
+ b c: *{
a: 2
} | {
a : 3
@@ -376,19 +396,19 @@
}, {
desc: "simple disambiguation conflict",
in: `
- a: "a" | "b"
- b: "b" | "a"
+ a: *"a" | "b"
+ b: *"b" | "a"
c: a & b
`,
- out: `<0>{a: "a", b: "b", c: _|_(("a"! | "b"!):ambiguous disjunction)}`,
+ out: `<0>{a: "a", b: "b", c: _|_((*"a" | *"b"):more than one default remaining ("a" and "b"))}`,
}, {
desc: "disambiguation non-conflict",
in: `
- a: "a" | ("b" | "c")
- b: ("a" | "b") | "c"
+ a: *"a" | ("b" | "c")
+ b: (*"a" | "b") | "c"
c: a & b
`,
- out: `<0>{a: "a", b: "a", c: "a"}`,
+ out: `<0>{a: "a", b: _|_(((*"a" | "b") | "c"):more than one element remaining ((*"a" | "b") and "c")), c: "a"}`,
}}
rewriteHelper(t, testCases, evalFull)
}
@@ -451,15 +471,15 @@
in: `
a: [2][0]
b: {foo:"bar"}["foo"]
- c: (l|{"3":3})["3"]
- d: ([]|[1])[0]
+ c: (*l|{"3":3})["3"]
+ d: (*[]|[1])[0]
l: []
e1: [2][""]
e2: 2[2]
e3: [][true]
e4: [1,2,3][3]
e5: [1,2,3][-1]
- e6: ([]|{})[1]
+ e6: (*[]|{})[1]
`,
out: `<0>{a: 2, b: "bar", c: _|_("3":invalid list index "3" (type string)), l: [], d: _|_([]:index 0 out of bounds), e1: _|_("":invalid list index "" (type string)), e2: _|_(2:invalid operation: 2[2] (type number does not support indexing)), e3: _|_(true:invalid list index true (type bool)), e4: _|_([1,2,3]:index 3 out of bounds), e5: _|_(-1:invalid list index -1 (index must be non-negative)), e6: _|_([]:index 1 out of bounds)}`,
}, {
@@ -810,19 +830,19 @@
in: `
a: 8000.9
a: 7080 | int`,
- out: `<0>{a: _|_(empty disjunction after evaluation)}`,
+ out: `<0>{a: _|_((8000.9 & (7080 | int)):empty disjunction: cannot unify numbers 7080 and 8000.9)}`,
}, {
desc: "resolve all disjunctions",
in: `
service <Name>: {
- name: Name | string
- port: 7080 | int
+ name: string | *Name
+ port: int | *7080
}
service foo: _
service bar: { port: 8000 }
service baz: { name: "foobar" }
`,
- out: `<0>{service: <1>{<>: <2>(Name: string)-><3>{name: (<2>.Name | string), port: (7080 | int)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
+ out: `<0>{service: <1>{<>: <2>(Name: string)-><3>{name: (string | *<2>.Name), port: (int | *7080)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
}, {
desc: "field templates",
in: `
@@ -831,7 +851,7 @@
k: 1
}
b: {
- <x>: { x: 0, y: 1 | int }
+ <x>: { x: 0, y: *1 | int }
v: {}
w: { y: 0 }
}
@@ -842,7 +862,7 @@
bar: _
}
`,
- out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`,
+ out: `<0>{a: <1>{<>: <2>(name: string)->int, k: 1}, b: <3>{<>: <4>(x: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{<>: <10>(Name: string)-><11>{name: <10>.Name, y: 1}, foo: <12>{name: "foo", y: 1}, bar: <13>{name: "bar", y: 1}}}`,
}, {
desc: "field comprehension",
in: `
@@ -952,7 +972,7 @@
}, {
desc: "disjunctions of lists",
in: `
- l: [ int, int ] | [ string, string ]
+ l: *[ int, int ] | [ string, string ]
l1: [ "a", "b" ]
l2: l & [ "c", "d" ]
@@ -980,8 +1000,8 @@
service <Name>: {
type: "service"
- name: Name | string
- port: 7080 | int
+ name: *Name | string
+ port: *7080 | int
}
service foo: {}
service bar: { port: 8000 }
@@ -993,7 +1013,7 @@
`<3>{type: "service", name: "foobar", port: 7080}], ` +
`service: <4>{` +
- `<>: <5>(Name: string)-><6>{type: "service", name: (<5>.Name | string), port: (7080 | int)}, ` +
+ `<>: <5>(Name: string)-><6>{type: "service", name: (*<5>.Name | string), port: (*7080 | int)}, ` +
`foo: <7>{type: "service", name: "foo", port: 7080}, ` +
`bar: <8>{type: "service", name: "bar", port: 8000}, ` +
`baz: <9>{type: "service", name: "foobar", port: 7080}}}`,
@@ -1026,7 +1046,7 @@
IP: 4*[ 0..255 ]
Private:
- [ 192, 168, 0..255, 0..255 ] |
+ *[ 192, 168, 0..255, 0..255 ] |
[ 10, 0..255, 0..255, 0..255] |
[ 172, 16..32, 0..255, 0..255 ]
diff --git a/cue/rewrite.go b/cue/rewrite.go
index b3b328f..6e28522 100644
--- a/cue/rewrite.go
+++ b/cue/rewrite.go
@@ -182,7 +182,7 @@
changed := false
for i, d := range x.values {
v := rewrite(ctx, d.val, fn)
- values[i] = dValue{v, d.ambiguous}
+ values[i] = dValue{v, d.marked}
changed = changed || v != d.val
}
if !changed {
diff --git a/cue/value.go b/cue/value.go
index 95abb6c..4397b74 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -1022,22 +1022,8 @@
}
type dValue struct {
- val value
- ambiguous bool
-}
-
-// makeDisjunction constructs a disjunction linked list.
-func makeDisjunction(ctx *context, n ast.Expr, a, b value) *disjunction {
- d, ok := a.(*disjunction)
- if !ok {
- d = &disjunction{newExpr(n), []dValue{{val: a}}}
- }
- if o, ok := b.(*disjunction); ok {
- d.values = append(d.values, o.values...)
- } else {
- d.values = append(d.values, dValue{val: b})
- }
- return d
+ val value
+ marked bool
}
func (x *disjunction) kind() kind {
@@ -1054,21 +1040,58 @@
func (x *disjunction) Pos() token.Pos { return x.values[0].val.Pos() }
// add add a value to the disjunction. It is assumed not to be a disjunction.
-func (x *disjunction) add(ctx *context, v value, ambiguous bool) {
- if !isBottom(v) {
- x.values = append(x.values, dValue{v, ambiguous})
- }
+func (x *disjunction) add(ctx *context, v value, marked bool) {
+ x.values = append(x.values, dValue{v, marked})
}
-// simplify unwraps the disjunction if necessary.
-func (x *disjunction) simplify(ctx *context, src source) value {
- switch len(x.values) {
- case 0:
- return ctx.mkErr(src, "empty disjunction after evaluation")
- case 1:
- return x.values[0].val
+// normalize removes redundant element from unification.
+// x must already have been evaluated.
+func (x *disjunction) normalize(ctx *context, src source) mVal {
+ less := func(ctx *context, lt, gt dValue) bool {
+ if isBottom(lt.val) {
+ return true
+ }
+ return (!lt.marked || gt.marked) && subsumes(ctx, gt.val, lt.val, 0)
}
- return x
+ k := 0
+outer:
+ for i, v := range x.values {
+ if isBottom(v.val) {
+ continue
+ }
+ for j, w := range x.values {
+ if i == j {
+ continue
+ }
+ if less(ctx, v, w) && (!less(ctx, w, v) || j < i) {
+ // strictly subsumed, or equal and and the equal element was
+ // processed earlier.
+ continue outer
+ }
+ }
+ // If there was a three-way equality, an element w, where w == v could
+ // already have been added.
+ for j := 0; j < k; j++ {
+ if less(ctx, v, x.values[j]) {
+ continue outer
+ }
+ }
+ x.values[k] = v
+ k++
+ }
+
+ switch k {
+ case 0:
+ // Empty disjunction. All elements must be errors.
+ // Take the first error as an example.
+ str := fmt.Sprintf("empty disjunction: %v", x.values[0].val)
+ return mVal{ctx.mkErr(src, str), false}
+ case 1:
+ v := x.values[0]
+ return mVal{v.val.(evaluated), v.marked}
+ }
+ x.values = x.values[:k]
+ return mVal{x, false}
}
type listComprehension struct {
diff --git a/doc/tutorial/basics/coalesce.md b/doc/tutorial/basics/coalesce.md
index 082708a..dde8c77 100644
--- a/doc/tutorial/basics/coalesce.md
+++ b/doc/tutorial/basics/coalesce.md
@@ -29,11 +29,11 @@
```
list: [ "Cat", "Mouse", "Dog" ]
-a: list[0] | "None"
-b: list[5] | "None"
+a: *list[0] | "None"
+b: *list[5] | "None"
n: [null]
-v: n[0] & string | "default"
+v: *n[0] & string | "default"
```
<!-- result -->
diff --git a/doc/tutorial/basics/defaults.md b/doc/tutorial/basics/defaults.md
index a46825d..c65cb67 100644
--- a/doc/tutorial/basics/defaults.md
+++ b/doc/tutorial/basics/defaults.md
@@ -19,11 +19,11 @@
<!-- CUE editor -->
```
// any positive number, 1 is the default
-replicas: 1 | uint
+replicas: uint | *1
// the default value is ambiguous
-protocol: "tcp" | "udp"
-protocol: "udp" | "tcp"
+protocol: *"tcp" | "udp"
+protocol: *"udp" | "tcp"
```
<!-- result -->
diff --git a/doc/tutorial/basics/templates.md b/doc/tutorial/basics/templates.md
index 3748cdb..bd40c2a 100644
--- a/doc/tutorial/basics/templates.md
+++ b/doc/tutorial/basics/templates.md
@@ -18,7 +18,7 @@
// The name of each element is bound to Name and visible in the struct.
job <Name>: {
name: Name
- replicas: 1 | uint
+ replicas: uint | *1
command: string
}
diff --git a/doc/tutorial/kubernetes/manual/services/cloud.cue b/doc/tutorial/kubernetes/manual/services/cloud.cue
index 27fdb1a..4ba6142 100644
--- a/doc/tutorial/kubernetes/manual/services/cloud.cue
+++ b/doc/tutorial/kubernetes/manual/services/cloud.cue
@@ -12,11 +12,10 @@
}
deployment <Name>: _base & {
- name: Name | string
-// jba: why do you need to write "Name | string"? Doesn't the grammar require that the value
-// of <Name> is a string?
+ // Allow any string, but take Name by default.
+ name: string | *Name
kind: "deployment" | "stateful" | "daemon"
- replicas: 1 | int
+ replicas: int | *1
image: string
@@ -36,10 +35,10 @@
envSpec: {"\(k)" value: v for k, v in env}
volume <Name>: {
- name: Name | string
+ name: string | *Name
mountPath: string
- subPath: null | string
- readOnly: false | true
+ subPath: string | *null
+ readOnly: *false | true
kubernetes: {}
}
}
@@ -48,11 +47,11 @@
name: Name | string
port <Name>: {
- name: Name | string
+ name: string | *Name
port: int
- targetPort: port | int
- protocol: "TCP" | "UDP"
+ targetPort: int | *port
+ protocol: *"TCP" | "UDP"
}
kubernetes: {}
@@ -66,10 +65,11 @@
// Copy over all ports exposed from containers.
port "\(Name)": {
- port: Port | int
-// jba: Port must be defined, so why do you need "| int"?
- targetPort: Port | int
-// jba: I don't think you need targetPort, because it's defined above in terms of port.
+ // Set default external port to Port.
+ port: int | *Port
+ targetPort: int | *Port
+ // TODO(verify): jba: I don't think you need targetPort, because it's defined above in terms of port.
+ // Should probably be Port fixed.
} for Name, Port in spec.expose.port
// Copy over the labels
diff --git a/doc/tutorial/kubernetes/manual/services/frontend/kube.cue b/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
index b01c302..c8d47e8 100644
--- a/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
+++ b/doc/tutorial/kubernetes/manual/services/frontend/kube.cue
@@ -3,7 +3,7 @@
_base label component: "frontend"
deployment <Name>: {
- expose port http: 7080 | int
+ expose port http: *7080 | int
kubernetes spec template metadata annotations: {
"prometheus.io.scrape": "true"
"prometheus.io.port": "\(expose.port.http)"
diff --git a/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue b/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
index 00a5466..40344c0 100644
--- a/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
+++ b/doc/tutorial/kubernetes/manual/services/kitchen/kube.cue
@@ -30,16 +30,16 @@
// Volumes
volume "\(name)-disk": {
name: string
- mountPath: "/logs" | string
+ mountPath: *"/logs" | string
spec gcePersistentDisk: {
- pdName: name | string
+ pdName: *name | string
fsType: "ext4"
}
}
volume "secret-\(name)": {
- mountPath: "/etc/certs" | string
+ mountPath: *"/etc/certs" | string
readOnly: true
- spec secret secretName: "\(name)-secrets" | string
+ spec secret secretName: *"\(name)-secrets" | string
}
}
diff --git a/doc/tutorial/kubernetes/quick/services/frontend/kube.cue b/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
index 58f084c..ce4c25e 100644
--- a/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/frontend/kube.cue
@@ -8,6 +8,6 @@
"prometheus.io.port": "\(spec.containers[0].ports[0].containerPort)"
}
spec containers: [{
- ports: [{containerPort: 7080 | int}] // 7080 is the default
+ ports: [{containerPort: *7080 | int}] // 7080 is the default
}]
}
diff --git a/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue b/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
index e213621..c4639d8 100644
--- a/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/kitchen/kube.cue
@@ -23,21 +23,21 @@
_hasDisks: true | bool
volumes: [{
- name: "\(Name)-disk" | string
- gcePersistentDisk pdName: "\(Name)-disk" | string
+ name: *"\(Name)-disk" | string
+ gcePersistentDisk pdName: *"\(Name)-disk" | string
gcePersistentDisk fsType: "ext4"
}, {
- name: "secret-\(Name)" | string
- secret secretName: "\(Name)-secrets" | string
+ name: *"secret-\(Name)" | string
+ secret secretName: *"\(Name)-secrets" | string
}, ...] if _hasDisks
containers: [{
volumeMounts: [{
- name: "\(Name)-disk" | string
- mountPath: "/logs" | string
+ name: *"\(Name)-disk" | string
+ mountPath: *"/logs" | string
}, {
- mountPath: "/etc/certs" | string
- name: "secret-\(Name)" | string
+ mountPath: *"/etc/certs" | string
+ name: *"secret-\(Name)" | string
readOnly: true
}, ...]
}] if _hasDisks // field comprehension using just "if"
diff --git a/doc/tutorial/kubernetes/quick/services/kube.cue b/doc/tutorial/kubernetes/quick/services/kube.cue
index a84a50e..09688f5 100644
--- a/doc/tutorial/kubernetes/quick/services/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/kube.cue
@@ -15,8 +15,8 @@
// Any port has the following properties.
ports: [...{
port: int
- protocol: "TCP" | "UDP" // from the Kubernetes definition
- name: "client" | string
+ protocol: *"TCP" | "UDP" // from the Kubernetes definition
+ name: *"client" | string
}]
selector: metadata.labels // we want those to be the same
}
@@ -66,7 +66,7 @@
// for all ports defined in all containers.
_spec spec template spec containers: [...{
ports: [...{
- _export: true | false // include the port in the service
+ _export: *true | false // include the port in the service
}]
}]
@@ -75,8 +75,8 @@
spec ports: [ {
Port = p.containerPort // Port is an alias
- port: Port | int
- targetPort: Port | int
+ port: *Port | int
+ targetPort: *Port | int
} for c in v.spec.template.spec.containers
for p in c.ports
if p._export ]