From 1370f0a46925482af80c0d6b817a86a0a15b0aec Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Wed, 15 Apr 2020 11:27:13 +0200 Subject: [PATCH] cue: implement "front-style" list comprehensions This now also allows any of the non-JSON keywords to be used as references. Previously, these were already supported as field names. Issue #339 Issue #165 Change-Id: I721d054c8220ba3536f680fe2e3e502a62f99b6b Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5683 Reviewed-by: Marcel van Lohuizen --- cmd/cue/cmd/fix_test.go | 8 + cmd/cue/cmd/testdata/script/legacy.txt | 16 +- cue/ast.go | 29 + cue/ast/ast.go | 5 +- cue/ast_test.go | 2 +- cue/export_test.go | 4 +- cue/format/node.go | 85 ++- cue/format/testdata/expressions.golden | 15 +- cue/format/testdata/expressions.input | 9 +- cue/parser/parser.go | 149 +++-- cue/parser/parser_test.go | 10 +- cue/resolve_test.go | 15 +- cue/types_test.go | 4 +- .../basics/6_expressions/40_listcomp.txt | 2 +- doc/tutorial/kubernetes/README.md | 25 +- .../kubernetes/manual/services/cloud.cue | 2 +- .../kubernetes/manual/services/k8s.cue | 31 +- .../kubernetes/manual/services/kube_tool.cue | 2 +- .../kubernetes/manual/services/ls_tool.cue | 6 +- .../kubernetes/quick/services/kube.cue | 14 +- .../kubernetes/quick/services/kube_tool.cue | 2 +- .../kubernetes/quick/services/ls_tool.cue | 5 +- doc/tutorial/kubernetes/testdata/manual.out | 594 ++++++++++-------- encoding/jsonschema/decode.go | 2 +- internal/encoding/json/encode_test.go | 4 +- 25 files changed, 631 insertions(+), 409 deletions(-) diff --git a/cmd/cue/cmd/fix_test.go b/cmd/cue/cmd/fix_test.go index 198c84465..26c0cb212 100644 --- a/cmd/cue/cmd/fix_test.go +++ b/cmd/cue/cmd/fix_test.go @@ -53,6 +53,14 @@ a: { X1="in": 3 ref: X1 & x } +`, + }, { + in: ` + y: [1, 2, 3, 4] + a: [ x for x in y ] + `, + out: `y: [1, 2, 3, 4] +a: [ for x in y { x } ] `, // }, { // name: "slice", diff --git a/cmd/cue/cmd/testdata/script/legacy.txt b/cmd/cue/cmd/testdata/script/legacy.txt index 8cb4a6e87..8fee102bc 100644 --- a/cmd/cue/cmd/testdata/script/legacy.txt +++ b/cmd/cue/cmd/testdata/script/legacy.txt @@ -1,14 +1,16 @@ -! cue eval foo.cue +cue eval foo.cue cmp stderr expect-stderr -! cue export foo.cue +cue export foo.cue cmp stderr expect-stderr -! cue fmt foo.cue -cmp stderr expect-stderr +cue fmt foo.cue +cmp foo.cue bar.cue -- expect-stderr -- -missing ',' in struct literal: - ./foo.cue:1:8 -- foo.cue -- -"x": 3 for x in a +a: [1] +b: [ x for x in a ] +-- bar.cue -- +a: [1] +b: [ for x in a { x } ] diff --git a/cue/ast.go b/cue/ast.go index fa1331ca3..04058b2d5 100644 --- a/cue/ast.go +++ b/cue/ast.go @@ -301,10 +301,39 @@ func (v *astVisitor) walk(astNode ast.Node) (ret value) { parent: v, } + if len(n.Elts) == 1 { + if c, ok := n.Elts[0].(*ast.Comprehension); ok { + yielder := &yield{baseValue: newExpr(c.Value)} + lc := &listComprehension{ + newExpr(c), + wrapClauses(v, yielder, c.Clauses), + } + // we don't support key for lists (yet?) + + // TODO(hack): unwrap struct lit if embedding of one element. + // We do this as we do not yet support embedding of scalar + // values in general. This prohibits: + // - having fields alongside embedded values + // - having more than one embedded value. + // The latter would not be too hard to circumvent. + expr := c.Value + if s, ok := expr.(*ast.StructLit); ok && len(s.Elts) == 1 { + if e, ok := s.Elts[0].(*ast.EmbedDecl); ok { + expr = e.Expr + } + } + yielder.value = v.walk(expr) + return lc + } + } + elts, ellipsis := internal.ListEllipsis(n) arcs := []arc{} for i, e := range elts { + if _, ok := e.(*ast.Comprehension); ok { + return v.errf(e, "comprehensions must be a single element within list (for now)") + } elem := v1.walk(e) if elem == nil { // TODO: it would be consistent to allow aliasing in lists diff --git a/cue/ast/ast.go b/cue/ast/ast.go index 3238a9971..b33204d7b 100644 --- a/cue/ast/ast.go +++ b/cue/ast/ast.go @@ -347,10 +347,11 @@ func (a *Alias) End() token.Pos { return a.Expr.End() } // A Comprehension node represents a comprehension declaration. type Comprehension struct { Clauses []Clause // There must be at least one clause. - Value Expr // Must be a struct + Value Expr // Must be a struct TODO: change to Struct comments decl + expr // TODO: only allow Comprehension in "Embedding" productions. } func (x *Comprehension) Pos() token.Pos { return getPos(x) } @@ -551,6 +552,8 @@ func NewStruct(fields ...interface{}) *StructLit { // A ListLit node represents a literal list. type ListLit struct { Lbrack token.Pos // position of "[" + + // TODO: change to embedding or similar. Elts []Expr // list of composite elements; or nil Rbrack token.Pos // position of "]" diff --git a/cue/ast_test.go b/cue/ast_test.go index 3cc17bd49..cb2e51916 100644 --- a/cue/ast_test.go +++ b/cue/ast_test.go @@ -313,7 +313,7 @@ alias "Y" redeclared in same scope: out: `<0>{a: <1>{ <2>for k, v in <0>.b yield <3>{""+<2>.v+"": <2>.v}}, b: <4>{a: "aa", b: "bb", c: "cc"}}`, }, { in: ` - a: [ v for _, v in b ] + a: [ for _, v in b { v } ] b: { a: 1, b: 2, c: 3 } `, out: `<0>{a: [ <1>for _, v in <0>.b yield <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`, diff --git a/cue/export_test.go b/cue/export_test.go index 6967acb5f..88c5b62db 100644 --- a/cue/export_test.go +++ b/cue/export_test.go @@ -251,11 +251,11 @@ func TestExport(t *testing.T) { }`), }, { raw: true, - in: `{ a: [1, 2], b: [ v for k, v in a ] }`, + in: `{ a: [1, 2], b: [ for k, v in a { v }] }`, out: unindent(` { a: [1, 2] - b: [ v for k, v in a ] + b: [ for k, v in a { v } ] }`), }, { raw: true, diff --git a/cue/format/node.go b/cue/format/node.go index d306d70d1..ba6797d8b 100644 --- a/cue/format/node.go +++ b/cue/format/node.go @@ -184,7 +184,36 @@ func (f *formatter) walkClauseList(list []ast.Clause, ws whiteSpace) { f.after(nil) } -func (f *formatter) walkExprList(list []ast.Expr, depth int) { +func (f *formatter) walkListElems(list []ast.Expr) { + f.before(nil) + for _, x := range list { + f.before(x) + switch n := x.(type) { + case *ast.Comprehension: + f.walkClauseList(n.Clauses, blank) + f.print(blank, nooverride) + f.expr(n.Value) + + case *ast.Ellipsis: + f.ellipsis(n) + + case *ast.Alias: + f.expr(n.Ident) + f.print(n.Equal, token.BIND) + f.expr(n.Expr) + + // TODO: ast.CommentGroup: allows comment groups in ListLits. + + case ast.Expr: + f.exprRaw(n, token.LowestPrec, 1) + } + f.print(comma, blank) + f.after(x) + } + f.after(nil) +} + +func (f *formatter) walkArgsList(list []ast.Expr, depth int) { f.before(nil) for _, x := range list { f.before(x) @@ -235,7 +264,6 @@ func (f *formatter) inlineField(n *ast.Field) *ast.Field { } func (f *formatter) decl(decl ast.Decl) { - if decl == nil { return } @@ -307,14 +335,6 @@ func (f *formatter) decl(decl ast.Decl) { f.print(formfeed) } - case *ast.Comprehension: - if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline { - f.print(formfeed) - } - f.walkClauseList(n.Clauses, blank) - f.print(blank, nooverride) - f.expr(n.Value) - case *ast.BadDecl: f.print(n.From, "*bad decl*", declcomma) @@ -350,6 +370,29 @@ func (f *formatter) decl(decl ast.Decl) { f.expr(n.Expr) f.print(newline, noblank) + case *ast.Attribute: + f.print(n.At, n) + + case *ast.CommentGroup: + f.print(newsection) + f.printComment(n) + f.print(newsection) + + case ast.Expr: + f.embedding(n) + } +} + +func (f *formatter) embedding(decl ast.Expr) { + switch n := decl.(type) { + case *ast.Comprehension: + if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline { + f.print(formfeed) + } + f.walkClauseList(n.Clauses, blank) + f.print(blank, nooverride) + f.expr(n.Value) + case *ast.Ellipsis: f.ellipsis(n) @@ -362,13 +405,10 @@ func (f *formatter) decl(decl ast.Decl) { f.expr(n.Expr) f.print(declcomma) // implied - case *ast.Attribute: - f.print(n.At, n) + // TODO: ast.CommentGroup: allows comment groups in ListLits. - case *ast.CommentGroup: - f.print(newsection) - f.printComment(n) - f.print(newsection) + case ast.Expr: + f.exprRaw(n, token.LowestPrec, 1) } } @@ -570,7 +610,7 @@ func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) { } wasIndented := f.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) f.print(x.Lparen, token.LPAREN) - f.walkExprList(x.Args, depth) + f.walkArgsList(x.Args, depth) f.print(trailcomma, noblank, x.Rparen, token.RPAREN) if wasIndented { f.print(unindent) @@ -612,7 +652,7 @@ func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) { case *ast.ListLit: f.print(x.Lbrack, token.LBRACK, indent) - f.walkExprList(x.Elts, 1) + f.walkListElems(x.Elts) f.print(trailcomma, noblank) f.visitComments(f.current.pos) f.matchUnindent() @@ -623,9 +663,16 @@ func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) { case *ast.ListComprehension: f.print(x.Lbrack, token.LBRACK, blank, indent) - f.expr(x.Expr) f.print(blank) f.walkClauseList(x.Clauses, blank) + f.print(blank, nooverride) + if _, ok := x.Expr.(*ast.StructLit); ok { + f.expr(x.Expr) + } else { + f.print(token.LBRACE, blank) + f.expr(x.Expr) + f.print(blank, token.RBRACE) + } f.print(unindent, f.wsOverride(blank), x.Rbrack, token.RBRACK) default: diff --git a/cue/format/testdata/expressions.golden b/cue/format/testdata/expressions.golden index 0dbc65851..019abde50 100644 --- a/cue/format/testdata/expressions.golden +++ b/cue/format/testdata/expressions.golden @@ -14,7 +14,7 @@ package expressions alias = 3.14 "g\("en")"?: 4 - alias2 = foo + alias2 = foo // with comment aaalias = foo b: bar @@ -140,13 +140,16 @@ package expressions e: [...int] e: [...int] e: [...int | float] - e: [ x for x in someObject if x > 9 ] - e: [ x + e: [ for x in someObject if x > 9 { + x + }] + e: [ for x in someObject if x > 9 { x } ] + e: [ for x in someObject - if x > 9 ] - e: [ x + if x > 9 { x } ] + e: [ for x in someObject - if x > 9 + if x > 9 { x } ] for k, v in someObject { "\(k)": v diff --git a/cue/format/testdata/expressions.input b/cue/format/testdata/expressions.input index 8b19de6b3..d153771ec 100644 --- a/cue/format/testdata/expressions.input +++ b/cue/format/testdata/expressions.input @@ -14,7 +14,7 @@ package expressions alias = 3.14 "g\("en")"?: 4 - alias2 = foo + alias2 = foo // with comment aaalias = foo b: bar @@ -138,6 +138,9 @@ package expressions e: [...int] e: [...int,] e: [...int | float] + e: [ for x in someObject if x > 9 { + x + }] e: [ x for x in someObject if x > 9 ] e: [ x for x in someObject @@ -159,11 +162,11 @@ package expressions "\(k)":v } } - + e: { for k, v in someObject if k > "a" {"\(k)":v} } e: { for k, v in someObject if k > "a" { "\(k)":v }} - + e: { for k, v in someObject if k > "a" { diff --git a/cue/parser/parser.go b/cue/parser/parser.go index eb3a06a4d..d9ea14c6e 100644 --- a/cue/parser/parser.go +++ b/cue/parser/parser.go @@ -368,6 +368,8 @@ func (p *parser) next() { } } +// assertV0 indicates the last version at which a certain feature was +// supported. func (p *parser) assertV0(pos token.Pos, minor, patch int, name string) { v := version0(minor, patch) if p.version != 0 && p.version > v { @@ -539,6 +541,16 @@ func (p *parser) parseIdent() *ast.Ident { return ident } +func (p *parser) parseKeyIdent() *ast.Ident { + c := p.openComments() + pos := p.pos + name := p.lit + p.next() + ident := &ast.Ident{NamePos: pos, Name: name} + c.closeNode(p, ident) + return ident +} + // ---------------------------------------------------------------------------- // Expressions @@ -589,6 +601,11 @@ func (p *parser) parseOperand() (expr ast.Expr) { Lparen: lparen, X: x, Rparen: rparen} + + default: + if p.tok.IsKeyword() { + return p.parseKeyIdent() + } } // we have an error @@ -681,7 +698,7 @@ func (p *parser) parseFieldList() (list []ast.Decl) { for p.tok != token.RBRACE && p.tok != token.ELLIPSIS && p.tok != token.EOF { switch p.tok { - case token.FOR, token.IF, token.LET: + case token.FOR, token.IF: list = append(list, p.parseComprehension()) case token.ATTRIBUTE: @@ -726,33 +743,42 @@ func (p *parser) parseComprehension() (decl ast.Decl) { opt := token.NoPos tok = p.tok pos = p.pos - if p.tok == token.OPTION { + switch p.tok { + case token.COMMA, token.EOF: + decl = &ast.EmbedDecl{Expr: lab} + + case token.OPTION: opt = pos p.next() tok = p.tok pos = p.pos - } - p.next() - rhs := p.parseRHS() - - // Field or alias. - switch tok { - case token.BIND: - decl = &ast.Alias{Ident: lab, Equal: pos, Expr: rhs} - case token.ISA, token.COLON: - decl = &ast.Field{ - Label: lab, - Optional: opt, - TokenPos: pos, - Token: tok, - Value: rhs, - Attrs: p.parseAttributes(), - } + + fallthrough + default: - decl = &ast.BadDecl{From: lab.Pos(), To: rhs.End()} + p.next() + rhs := p.parseRHS() + + // Field or alias. + switch tok { + case token.BIND: + decl = &ast.Alias{Ident: lab, Equal: pos, Expr: rhs} + case token.ISA, token.COLON: + decl = &ast.Field{ + Label: lab, + Optional: opt, + TokenPos: pos, + Token: tok, + Value: rhs, + Attrs: p.parseAttributes(), + } + default: + decl = &ast.BadDecl{From: lab.Pos(), To: rhs.End()} + } } + fc.closeNode(p, decl) - if p.atComma("struct literal", token.RBRACE) { // TODO: may be EOF + if p.atComma("struct literal", token.RBRACE, token.EOF) { p.next() } @@ -833,14 +859,15 @@ func (p *parser) parseField() (decl ast.Decl) { p.expectComma() // sync parser. fallthrough - case token.RBRACE: + case token.RBRACE, token.EOF: if i == 0 { if a, ok := expr.(*ast.Alias); ok { return a } switch tok { case token.IDENT, token.LBRACK, token.STRING, token.INTERPOLATION, - token.NULL, token.TRUE, token.FALSE: + token.NULL, token.TRUE, token.FALSE, + token.FOR, token.IF, token.LET, token.IN: return &ast.EmbedDecl{Expr: expr} } } @@ -940,7 +967,8 @@ func (p *parser) parseLabel(rhs bool) (label ast.Label, expr ast.Expr, ok bool) tok := p.tok switch tok { case token.IDENT, token.STRING, token.INTERPOLATION, - token.NULL, token.TRUE, token.FALSE: + token.NULL, token.TRUE, token.FALSE, + token.FOR, token.IF, token.LET, token.IN: expr = p.parseExpr() switch x := expr.(type) { @@ -983,29 +1011,6 @@ func (p *parser) parseLabel(rhs bool) (label ast.Label, expr ast.Expr, ok bool) // Note: caller must verify this list is suitable as a label. label, ok = x, true } - - case token.IF, token.FOR, token.IN, token.LET: - // Keywords representing clauses. - pos := p.pos - ident := &ast.Ident{ - NamePos: pos, - Name: p.lit, - } - c := p.openComments() - p.next() - expr = c.closeExpr(p, ident) - - if p.tok != token.COLON && p.tok != token.ISA { - c = p.openComments() - p.errf(pos, "expected operand, found '%s'", ident.Name) - expr = &ast.BadExpr{From: pos, To: p.pos} - // Sync expression. - expr = p.parseBinaryExprTail(token.LowestPrec+1, expr) - expr = c.closeExpr(p, expr) - break - } - label = ident - ok = true } return label, expr, ok } @@ -1056,7 +1061,8 @@ func (p *parser) parseComprehensionClauses(first bool) (clauses []ast.Clause, c forPos := p.expect(token.FOR) if first { switch p.tok { - case token.COLON, token.ISA, token.BIND, token.OPTION: + case token.COLON, token.ISA, token.BIND, token.OPTION, + token.COMMA, token.EOF: return nil, c } } @@ -1085,7 +1091,8 @@ func (p *parser) parseComprehensionClauses(first bool) (clauses []ast.Clause, c ifPos := p.expect(token.IF) if first { switch p.tok { - case token.COLON, token.ISA, token.BIND, token.OPTION: + case token.COLON, token.ISA, token.BIND, token.OPTION, + token.COMMA, token.EOF: return nil, c } } @@ -1095,7 +1102,12 @@ func (p *parser) parseComprehensionClauses(first bool) (clauses []ast.Clause, c Condition: p.parseRHS(), })) + // TODO: // case token.LET: + // c := p.openComments() + // p.expect(token.LET) + // return nil, c + default: return clauses, nil } @@ -1118,6 +1130,7 @@ func (p *parser) parseList() (expr ast.Expr) { if clauses, _ := p.parseComprehensionClauses(false); clauses != nil { var expr ast.Expr + p.assertV0(p.pos, 1, 1, "old-style list comprehensions") if len(elts) != 1 { p.errf(lbrack.Add(1), "list comprehension must have exactly one element") } @@ -1170,6 +1183,12 @@ func (p *parser) parseListElements() (list []ast.Expr) { } } + for _, v := range list { + if _, ok := v.(*ast.Comprehension); ok && len(list) != 1 { + p.errf(v.Pos(), "multiple comprehensions per list not yet supported") + } + } + return } @@ -1180,7 +1199,37 @@ func (p *parser) parseListElement() (expr ast.Expr, ok bool) { c := p.openComments() defer func() { c.closeNode(p, expr) }() - expr = p.parseBinaryExprTail(token.LowestPrec+1, p.parseUnaryExpr()) + switch p.tok { + case token.FOR, token.IF: + tok := p.tok + pos := p.pos + clauses, fc := p.parseComprehensionClauses(true) + if clauses != nil { + sc := p.openComments() + expr := p.parseStruct() + sc.closeExpr(p, expr) + + if p.atComma("struct literal", token.RBRACK) { // TODO: may be EOF + p.next() + } + + return &ast.Comprehension{ + Clauses: clauses, + Value: expr, + }, true + } + + expr = &ast.Ident{ + NamePos: pos, + Name: tok.String(), + } + fc.closeNode(p, expr) + + default: + expr = p.parseUnaryExpr() + } + + expr = p.parseBinaryExprTail(token.LowestPrec+1, expr) expr = p.parseAlias(expr) // Enforce there is an explicit comma. We could also allow the diff --git a/cue/parser/parser_test.go b/cue/parser/parser_test.go index 92734f442..7e4f61b88 100644 --- a/cue/parser/parser_test.go +++ b/cue/parser/parser_test.go @@ -34,7 +34,7 @@ func TestParse(t *testing.T) { }, { "basic lits", `"a","b", 3,3.4,5,2_3`, `"a", "b", 3, 3.4, 5, 2_3`, }, { - "keyword basic lits", `true,false,null`, `true, false, null`, + "keyword basic lits", `true,false,null,for,in,if,let`, `true, false, null, for, in, if, let`, }, { "keywords as labels", `if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5`, @@ -239,9 +239,9 @@ func TestParse(t *testing.T) { "list comprehensions", `{ y: [1,2,3] - b: [ x for x in y if x == 1 ], + b: [ for x in y if x == 1 { x } ], }`, - `{y: [1, 2, 3], b: [x for x in y if x==1 ]}`, + `{y: [1, 2, 3], b: [for x in y if x==1 {x}]}`, }, { "field comprehensions", `{ @@ -443,11 +443,11 @@ bar: 2 `X1=[X2=<"d"]: {name: X2}, ` + `Y1=foo: {Y2=bar: [Y1, Y2]}`, }, { - desc: "error when keyword is used in expression", + desc: "allow keyword in expression", in: ` foo: in & 2 `, - out: "foo: <*ast.BadExpr>&2\nexpected operand, found 'in'", + out: "foo: in&2", }, { desc: "dot import", in: ` diff --git a/cue/resolve_test.go b/cue/resolve_test.go index 591b3c594..7126bf370 100644 --- a/cue/resolve_test.go +++ b/cue/resolve_test.go @@ -2248,22 +2248,21 @@ func TestFullEval(t *testing.T) { }, { desc: "list comprehension", in: ` - // a: [ k for k: v in b if k < "d" if v > b.a ] // TODO test error using common iso colon - a: [ k for k, v in b if k < "d" if v > b.a ] + a: [ for k, v in b if k < "d" if v > b.a { k }] b: { a: 1 b: 2 c: 3 d: 4 } - c: [ x for _, x in b for _, y in b if x < y ] - d: [ x for x, _ in a ] + c: [ for _, x in b for _, y in b if x < y { x } ] + d: [ for x, _ in a { x } ] `, out: `<0>{a: ["b","c"], b: <1>{a: 1, b: 2, c: 3, d: 4}, c: [1,1,1,2,2,3], d: [0,1]}`, }, { desc: "struct comprehension with template", in: ` - result: [ v for _, v in service ] + result: [ for _, v in service { v } ] service: [Name=string]: { name: *Name | string @@ -2336,7 +2335,7 @@ func TestFullEval(t *testing.T) { }, { desc: "complex interaction of groundness", in: ` - res: [ y & { d: "b" } for x in a for y in x ] + res: [ for x in a for y in x { y & { d: "b" } }] res: [ a.b.c & { d: "b" } ] a: b: [C=string]: { d: string, s: "a" + d } @@ -2584,7 +2583,7 @@ func TestFullEval(t *testing.T) { [jobID=string]: { } } - JobID :: or([ k for k, _ in jobs ]) + JobID :: or([ for k, _ in jobs { k } ]) } foo: Workflow & { @@ -2867,7 +2866,7 @@ func TestFullEval(t *testing.T) { desc: "alias reuse in nested scope", in: ` Foo :: { - X = or([ k for k, _ in {} ]) + X = or([ for k, _ in {} { k } ]) connection: [X]: X } A :: { diff --git a/cue/types_test.go b/cue/types_test.go index 1c90aec49..2320bc449 100644 --- a/cue/types_test.go +++ b/cue/types_test.go @@ -564,7 +564,7 @@ func TestList(t *testing.T) { value: `>=5*[1,2,3, ...int]`, err: "incomplete", }, { - value: `[x for x in y if x > 1] + value: `[for x in y if x > 1 { x }] y :: [1,2,3]`, res: "[2,3,]", }, { @@ -1191,7 +1191,7 @@ func TestDecode(t *testing.T) { dst: intList(), want: *intList(1, 2, 3), }, { - value: `[x for x in y if x > 1] + value: `[for x in y if x > 1 { x }] y :: [1,2,3]`, dst: intList(), want: *intList(2, 3), diff --git a/doc/tutorial/basics/6_expressions/40_listcomp.txt b/doc/tutorial/basics/6_expressions/40_listcomp.txt index 88eb17771..dbc5132c7 100644 --- a/doc/tutorial/basics/6_expressions/40_listcomp.txt +++ b/doc/tutorial/basics/6_expressions/40_listcomp.txt @@ -11,7 +11,7 @@ Lists can be created with list comprehensions. The example shows the use of `for` loops and `if` guards. -- listcomp.cue -- -[ x*x for x in items if x rem 2 == 0] +[ for x in items if x rem 2 == 0 { x*x } ] items :: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] diff --git a/doc/tutorial/kubernetes/README.md b/doc/tutorial/kubernetes/README.md index 19c86df3f..4c998e19b 100644 --- a/doc/tutorial/kubernetes/README.md +++ b/doc/tutorial/kubernetes/README.md @@ -498,13 +498,15 @@ for x in [deployment, daemonSet, statefulSet] for k, v in x { service: "\(k)": { spec: selector: v.spec.template.metadata.labels - spec: ports: [ { - Port = p.containerPort // Port is an alias - port: *Port | int - targetPort: *Port | int - } for c in v.spec.template.spec.containers + spec: ports: [ + for c in v.spec.template.spec.containers for p in c.ports - if p._export ] + if p._export { + Port = p.containerPort // Port is an alias + port: *Port | int + targetPort: *Port | int + } + ] } } EOF @@ -856,7 +858,7 @@ We create the tool file to do just that. $ cat < kube_tool.cue package kube -objects: [ x for v in objectSets for x in v ] +objects: [ for v in objectSets for x in v { x } ] objectSets: [ service, @@ -891,8 +893,9 @@ import ( command: ls: { task: print: cli.Print & { text: tabwriter.Write([ - "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" - for x in objects + for x in objects { + "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" + } ]) } @@ -1176,7 +1179,7 @@ The next step is to pull common fields, such as `image` to the top level. Arguments can be specified as a map. ``` arg: [string]: string - args: [ "-\(k)=\(v)" for k, v in arg ] | [...string] + args: [ for k, v in arg { "-\(k)=\(v)" } ] | [...string] ``` If order matters, users could explicitly specify the list as well. @@ -1281,7 +1284,7 @@ kubernetes: services: { metadata: labels: x.label spec: selector: x.label - spec: ports: [ p for p in x.port ] + spec: ports: [ for p in x.port { p } ] } } } diff --git a/doc/tutorial/kubernetes/manual/services/cloud.cue b/doc/tutorial/kubernetes/manual/services/cloud.cue index bf8619777..e16bbffbb 100644 --- a/doc/tutorial/kubernetes/manual/services/cloud.cue +++ b/doc/tutorial/kubernetes/manual/services/cloud.cue @@ -26,7 +26,7 @@ deployment: [Name=_]: _base & { port: [string]: int arg: [string]: string - args: [ "-\(k)=\(v)" for k, v in arg ] | [...string] + args: [ for k, v in arg { "-\(k)=\(v)" } ] | [...string] // Environment variables env: [string]: string diff --git a/doc/tutorial/kubernetes/manual/services/k8s.cue b/doc/tutorial/kubernetes/manual/services/k8s.cue index cefab64f5..381e48324 100644 --- a/doc/tutorial/kubernetes/manual/services/k8s.cue +++ b/doc/tutorial/kubernetes/manual/services/k8s.cue @@ -84,13 +84,13 @@ _k8sSpec: X: kubernetes: { image: X.image args: X.args if len(X.envSpec) > 0 { - env: [ {name: k} & v for k, v in X.envSpec ] + env: [ for k, v in X.envSpec {v, name: k} ] } - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] }] } @@ -98,7 +98,11 @@ _k8sSpec: X: kubernetes: { spec: template: spec: { if len(X.volume) > 0 { volumes: [ - v.kubernetes & {name: v.name} for v in X.volume + for v in X.volume { + v.kubernetes + + name: v.name + } ] } @@ -106,16 +110,17 @@ _k8sSpec: X: kubernetes: { // TODO: using conversions this would look like: // volumeMounts: [ k8s.VolumeMount(v) for v in d.volume ] if len(X.volume) > 0 { - volumeMounts: [ { - name: v.name - mountPath: v.mountPath - if v.subPath != null | true { - subPath: v.subPath - } - if v.readOnly { - readOnly: v.readOnly + volumeMounts: [ + for v in X.volume { + name: v.name + mountPath: v.mountPath + if v.subPath != null | true { + subPath: v.subPath + } + if v.readOnly { + readOnly: v.readOnly + } } - } for v in X.volume ] } }] diff --git a/doc/tutorial/kubernetes/manual/services/kube_tool.cue b/doc/tutorial/kubernetes/manual/services/kube_tool.cue index b41cea025..0ca9d0f0e 100644 --- a/doc/tutorial/kubernetes/manual/services/kube_tool.cue +++ b/doc/tutorial/kubernetes/manual/services/kube_tool.cue @@ -1,6 +1,6 @@ package kube -objects: [ x for v in objectSets for x in v ] +objects: [ for v in objectSets for x in v { x } ] objectSets: [ kubernetes.services, diff --git a/doc/tutorial/kubernetes/manual/services/ls_tool.cue b/doc/tutorial/kubernetes/manual/services/ls_tool.cue index d82e2af30..d5739ba3b 100644 --- a/doc/tutorial/kubernetes/manual/services/ls_tool.cue +++ b/doc/tutorial/kubernetes/manual/services/ls_tool.cue @@ -6,8 +6,10 @@ command: ls: { task: print: { kind: "print" Lines = [ - "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" - for x in objects ] + for x in objects { + "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" + } + ] text: strings.Join(Lines, "\n") } } diff --git a/doc/tutorial/kubernetes/quick/services/kube.cue b/doc/tutorial/kubernetes/quick/services/kube.cue index bf6ae39b4..394b15eab 100644 --- a/doc/tutorial/kubernetes/quick/services/kube.cue +++ b/doc/tutorial/kubernetes/quick/services/kube.cue @@ -95,12 +95,14 @@ for x in [deployment, daemonSet, statefulSet] for k, v in x { service: "\(k)": { spec: selector: v.spec.template.metadata.labels - spec: ports: [ { - Port = p.containerPort // Port is an alias - port: *Port | int - targetPort: *Port | int - } for c in v.spec.template.spec.containers + spec: ports: [ + for c in v.spec.template.spec.containers for p in c.ports - if p._export ] + if p._export { + Port = p.containerPort // Port is an alias + port: *Port | int + targetPort: *Port | int + }, + ] } } diff --git a/doc/tutorial/kubernetes/quick/services/kube_tool.cue b/doc/tutorial/kubernetes/quick/services/kube_tool.cue index 929ad340d..b9c92f3fe 100644 --- a/doc/tutorial/kubernetes/quick/services/kube_tool.cue +++ b/doc/tutorial/kubernetes/quick/services/kube_tool.cue @@ -1,6 +1,6 @@ package kube -objects: [ x for v in objectSets for x in v ] +objects: [ for v in objectSets for x in v { x } ] objectSets: [ service, diff --git a/doc/tutorial/kubernetes/quick/services/ls_tool.cue b/doc/tutorial/kubernetes/quick/services/ls_tool.cue index 1c73beba2..7fc52319e 100644 --- a/doc/tutorial/kubernetes/quick/services/ls_tool.cue +++ b/doc/tutorial/kubernetes/quick/services/ls_tool.cue @@ -9,8 +9,9 @@ import ( command: ls: { task: print: cli.Print & { text: tabwriter.Write([ - "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" - for x in objects + for x in objects { + "\(x.kind) \t\(x.metadata.labels.component) \t\(x.metadata.name)" + } ]) } diff --git a/doc/tutorial/kubernetes/testdata/manual.out b/doc/tutorial/kubernetes/testdata/manual.out index e13b62f0f..4f0b7329b 100644 --- a/doc/tutorial/kubernetes/testdata/manual.out +++ b/doc/tutorial/kubernetes/testdata/manual.out @@ -27,17 +27,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -46,13 +47,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -98,17 +100,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -117,13 +120,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -284,17 +288,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -303,13 +308,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -473,17 +479,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -492,13 +499,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -659,17 +667,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -678,13 +687,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -845,17 +855,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -864,13 +875,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -1034,17 +1046,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -1053,13 +1066,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -1220,17 +1234,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -1239,13 +1254,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -1409,17 +1425,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -1428,13 +1445,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -1480,17 +1498,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -1499,13 +1518,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -1651,17 +1671,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -1670,13 +1691,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2000,17 +2022,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2019,13 +2042,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2248,17 +2272,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2267,13 +2292,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2471,17 +2497,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2490,13 +2517,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2665,17 +2693,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2684,13 +2713,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2877,17 +2907,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2896,13 +2927,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -2948,17 +2980,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -2967,13 +3000,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -3255,17 +3289,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -3274,13 +3309,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -3559,17 +3595,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -3578,13 +3615,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -3842,17 +3880,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -3861,13 +3900,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -4124,17 +4164,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -4143,13 +4184,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -4410,17 +4452,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -4429,13 +4472,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -4696,17 +4740,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -4715,13 +4760,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -4929,17 +4975,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -4948,13 +4995,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -5029,17 +5077,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -5048,13 +5097,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -5316,17 +5366,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -5335,13 +5386,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -5569,17 +5621,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -5588,13 +5641,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -5836,17 +5890,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -5855,13 +5910,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -6555,17 +6611,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -6574,13 +6631,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -6626,17 +6684,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -6645,13 +6704,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -6942,17 +7002,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -6961,13 +7022,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -7152,17 +7214,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -7171,13 +7234,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { @@ -7792,17 +7856,18 @@ _k8sSpec: { name: X.name image: X.image args: X.args - ports: [ { + ports: [ for k, p in X.expose.port & X.port { name: k containerPort: p - } for k, p in X.expose.port & X.port ] + } ] if len(X.envSpec) > 0 { - env: [ { + env: [ for k, v in X.envSpec { name: k - } & v for k, v in X.envSpec ] + v + } ] } if len(X.volume) > 0 { - volumeMounts: [ { + volumeMounts: [ for v in X.volume { name: v.name mountPath: v.mountPath if v.subPath != null | true { @@ -7811,13 +7876,14 @@ _k8sSpec: { if v.readOnly { readOnly: v.readOnly } - } for v in X.volume ] + } ] } }] if len(X.volume) > 0 { - volumes: [ v.kubernetes & { + volumes: [ for v in X.volume { name: v.name - } for v in X.volume ] + v.kubernetes + } ] } } metadata: { diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go index 72bd2cbd9..db9007de7 100644 --- a/encoding/jsonschema/decode.go +++ b/encoding/jsonschema/decode.go @@ -419,7 +419,7 @@ func (s *state) value(n cue.Value) ast.Expr { default: if !n.IsConcrete() { - s.errf(n, "invalid non-concerte value") + s.errf(n, "invalid non-concrete value") } return n.Syntax(cue.Final()).(ast.Expr) } diff --git a/internal/encoding/json/encode_test.go b/internal/encoding/json/encode_test.go index 0bf28148d..e0432bbda 100644 --- a/internal/encoding/json/encode_test.go +++ b/internal/encoding/json/encode_test.go @@ -154,9 +154,9 @@ f4: { a: 1 b: 3 } - c: [1, [ x for x in m ]] + c: [1, [ for x in m { x } ]] `, - out: "json: unsupported node [x for x in m ] (*ast.ListComprehension)", + out: "json: unsupported node for x in m {x} (*ast.Comprehension)", }, { name: "disallowMultipleEmbeddings", in: `