cue/ast: add NewStruct helper
Change-Id: Ie6acb637416a2be06325362346b6aad0bc702ed7
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4902
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index cc74748..fdf4164 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -481,7 +481,7 @@
f.Decls = append(f.Decls, field)
for _, e := range pathElems[1:] {
newField := &ast.Field{Label: e}
- newVal := &ast.StructLit{Elts: []ast.Decl{newField}}
+ newVal := ast.NewStruct(newField)
field.Value = newVal
field = newField
}
diff --git a/cue/ast/ast.go b/cue/ast/ast.go
index 4138969..d723e24 100644
--- a/cue/ast/ast.go
+++ b/cue/ast/ast.go
@@ -17,6 +17,7 @@
package ast // import "cuelang.org/go/cue/ast"
import (
+ "fmt"
"strconv"
"strings"
@@ -461,6 +462,71 @@
expr
}
+// NewStruct creates a struct from the given fields.
+// A field is either a *Field, an *Elipsis or a Label, optionally followed by a
+// a token.OPTION to indicate the field is optional, optionally followed by a
+// token.ISA to indicate the field is a defintion followed by an expression
+// for the field value.
+// It will panic if a values not matching these patterns are given.
+// Useful for ASTs generated by code other than the CUE parser.
+func NewStruct(fields ...interface{}) *StructLit {
+ s := &StructLit{}
+ for i := 0; i < len(fields); i++ {
+ var (
+ label Label
+ optional = token.NoPos
+ tok = token.ILLEGAL
+ expr Expr
+ )
+
+ switch x := fields[i].(type) {
+ case *Field:
+ s.Elts = append(s.Elts, x)
+ continue
+ case *Ellipsis:
+ s.Elts = append(s.Elts, x)
+ continue
+ case Label:
+ label = x
+ case string:
+ label = NewString(x)
+ default:
+ panic(fmt.Sprintf("unsupported label type %T", x))
+ }
+
+ inner:
+ for i++; i < len(fields); i++ {
+ switch x := (fields[i]).(type) {
+ case Expr:
+ expr = x
+ break inner
+ case token.Token:
+ switch x {
+ case token.ISA:
+ tok = x
+ case token.OPTION:
+ optional = token.Blank.Pos()
+ case token.COLON, token.ILLEGAL:
+ default:
+ panic(fmt.Sprintf("invalid token %s", x))
+ }
+ default:
+ panic(fmt.Sprintf("unsupported expression type %T", x))
+ }
+ }
+ if expr == nil {
+ panic("label not matched with expression")
+ }
+ s.Elts = append(s.Elts, &Field{
+ Label: label,
+ Optional: optional,
+ Token: tok,
+ Value: expr,
+ })
+ }
+ return s
+}
+
// A ListLit node represents a literal list.
type ListLit struct {
Lbrack token.Pos // position of "["
diff --git a/cue/ast/astutil/apply_test.go b/cue/ast/astutil/apply_test.go
index c9d7241..09bc8f4 100644
--- a/cue/ast/astutil/apply_test.go
+++ b/cue/ast/astutil/apply_test.go
@@ -116,12 +116,7 @@
default:
c.InsertAfter(astutil.ApplyRecursively(&ast.Field{
Label: ast.NewIdent("iam"),
- Value: &ast.StructLit{Elts: []ast.Decl{
- &ast.Field{
- Label: ast.NewIdent("here"),
- Value: ast.NewIdent("new"),
- },
- }},
+ Value: ast.NewStruct(ast.NewIdent("here"), ast.NewIdent("new")),
}))
case "iam":
c.InsertAfter(&ast.Field{
diff --git a/cue/format/node_test.go b/cue/format/node_test.go
index b544f3b..caf366d 100644
--- a/cue/format/node_test.go
+++ b/cue/format/node_test.go
@@ -34,13 +34,9 @@
out string
}{{
desc: "label sequence for definition",
- node: &ast.Field{Label: ident("foo"), Value: &ast.StructLit{
- Elts: []ast.Decl{&ast.Field{
- Label: ident("bar"),
- Token: token.ISA,
- Value: &ast.StructLit{},
- }},
- }},
+ node: &ast.Field{Label: ident("foo"), Value: ast.NewStruct(
+ ident("bar"), token.ISA, &ast.StructLit{},
+ )},
// Force a new struct.
out: `foo: bar :: {
}`,
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index c4c5add..985c904 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -108,7 +108,7 @@
}
f = &ast.Field{
Label: ast.NewIdent(rootDefs),
- Value: &ast.StructLit{Elts: []ast.Decl{f}},
+ Value: ast.NewStruct(f),
}
ast.SetRelPos(f, token.NewSection)
s.definitions = append(s.definitions, f)
@@ -386,10 +386,7 @@
p0d("propertyNames", 6, func(n cue.Value, s *state) {
s.kind |= cue.StructKind
// [=~pattern]: _
- s.add(&ast.StructLit{Elts: []ast.Decl{&ast.Field{
- Label: ast.NewList(s.schema(n)),
- Value: ast.NewIdent("_"),
- }}})
+ s.add(ast.NewStruct(ast.NewList(s.schema(n)), ast.NewIdent("_")))
}),
p0("minProperties", func(n cue.Value, s *state) {
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index c644ff0..afa502d 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -226,9 +226,7 @@
add(ast.NewList(&ast.Ellipsis{}))
}
case "object":
- elps := &ast.Ellipsis{}
- st := &ast.StructLit{Elts: []ast.Decl{elps}}
- add(st)
+ add(ast.NewStruct(&ast.Ellipsis{}))
default:
s.errf(n, "unknown type %q", n)
}
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index b35e6ae..2cca457 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -528,7 +528,7 @@
name := p.ident(x.Position, x.Name)
f = &ast.Field{
Label: name,
- Value: &ast.StructLit{Elts: []ast.Decl{f}},
+ Value: ast.NewStruct(f),
}
addComments(f, i, x.Comment, x.InlineComment)