cue/ast: convenience constructors for common types
In line with NewIdent
Updated code to use them, providing evidence of
their usefulness.
Change-Id: I0ccea6cb7df852c6ee56436bd20e008267a14aee
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3242
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/fix.go b/cmd/cue/cmd/fix.go
index a3fd8a2..5406384 100644
--- a/cmd/cue/cmd/fix.go
+++ b/cmd/cue/cmd/fix.go
@@ -119,17 +119,11 @@
astutil.CopyMeta(lo, x.Low)
}
if hi == nil { // a[i:]
- hi = &ast.CallExpr{
- Fun: ast.NewIdent("len"),
- Args: []ast.Expr{x.X},
- }
+ hi = ast.NewCall(ast.NewIdent("len"), x.X)
astutil.CopyMeta(lo, x.High)
}
if pkg := c.Import("list"); pkg != nil {
- c.Replace(&ast.CallExpr{
- Fun: &ast.SelectorExpr{X: pkg, Sel: ast.NewIdent("Slice")},
- Args: []ast.Expr{x.X, lo, hi},
- })
+ c.Replace(ast.NewCall(ast.NewSel(pkg, "Slice"), x.X, lo, hi))
}
}
return true
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index fcc0d29..697d1ff 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -489,11 +489,8 @@
ident = nil
}
- path := fmt.Sprintf(`"encoding/%s"`, short)
- imports.Specs = append(imports.Specs, &ast.ImportSpec{
- Name: ident,
- Path: &ast.BasicLit{Kind: token.STRING, Value: path},
- })
+ imports.Specs = append(imports.Specs,
+ ast.NewImport(ident, "encoding/"+short))
}
}
f.Decls = append([]ast.Decl{imports}, f.Decls...)
@@ -709,15 +706,9 @@
// found a replacable string
dataField := h.uniqueName(name, "_", "cue_")
- f.Value = &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: h.altNames[enc.typ],
- Sel: ast.NewIdent("Marshal"),
- },
- Args: []ast.Expr{
- ast.NewIdent(dataField),
- },
- }
+ f.Value = ast.NewCall(
+ ast.NewSel(h.altNames[enc.typ], "Marshal"),
+ ast.NewIdent(dataField))
obj.Elts = append(obj.Elts, nil)
copy(obj.Elts[i+1:], obj.Elts[i:])
diff --git a/cue/ast/ast.go b/cue/ast/ast.go
index 8e7ec79..fb93b3f 100644
--- a/cue/ast/ast.go
+++ b/cue/ast/ast.go
@@ -17,6 +17,7 @@
package ast // import "cuelang.org/go/cue/ast"
import (
+ "strconv"
"strings"
"cuelang.org/go/cue/token"
@@ -409,6 +410,19 @@
Value string // literal string; e.g. 42, 0x7f, 3.14, 1_234_567, 1e-9, 2.4i, 'a', '\x7f', "foo", or '\m\n\o'
}
+// NewString creates a new BasicLit with a string value without position.
+// It quotes the given string.
+// Useful for ASTs generated by code other than the Go
+func NewString(str string) *BasicLit {
+ // TODO: use CUE quoting.
+ str = strconv.Quote(str)
+ return &BasicLit{Kind: token.STRING, ValuePos: token.NoPos, Value: str}
+}
+
+// TODO:
+// - use CUE-specific quoting (hoist functionality in export)
+// - NewBytes
+
// A Interpolation node represents a string or bytes interpolation.
type Interpolation struct {
label
@@ -481,6 +495,15 @@
Sel *Ident // field selector
}
+// NewSel creates a sequence of selectors.
+// Useful for ASTs generated by code other than the Go
+func NewSel(x Expr, sel ...string) Expr {
+ for _, s := range sel {
+ x = &SelectorExpr{X: x, Sel: NewIdent(s)}
+ }
+ return x
+}
+
// An IndexExpr node represents an expression followed by an index.
type IndexExpr struct {
comments
@@ -509,6 +532,12 @@
Rparen token.Pos // position of ")"
}
+// NewCall creates a new CallExpr.
+// Useful for ASTs generated by code other than the Go
+func NewCall(fun Expr, args ...Expr) *CallExpr {
+ return &CallExpr{Fun: fun, Args: args}
+}
+
// A UnaryExpr node represents a unary expression.
type UnaryExpr struct {
comments
@@ -633,6 +662,12 @@
EndPos token.Pos // end of spec (overrides Path.Pos if nonzero)
}
+func NewImport(name *Ident, importPath string) *ImportSpec {
+ importPath = strconv.Quote(importPath)
+ path := &BasicLit{Kind: token.STRING, Value: importPath}
+ return &ImportSpec{Name: name, Path: path}
+}
+
// Pos and End implementations for spec nodes.
func (s *ImportSpec) Pos() token.Pos { return getPos(s) }
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 96b4b63..3550a1e 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -451,7 +451,7 @@
}
func TestShadowing(t *testing.T) {
- spec := &ast.ImportSpec{Path: mustParseExpr(`"list"`).(*ast.BasicLit)}
+ spec := ast.NewImport(nil, "list")
testCases := []struct {
file *ast.File
want string
@@ -460,15 +460,8 @@
&ast.ImportDecl{Specs: []*ast.ImportSpec{spec}},
&ast.Field{
Label: mustParseExpr(`list`).(*ast.Ident),
- Value: &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: &ast.Ident{
- Name: "list",
- Node: spec,
- },
- Sel: ast.NewIdent("Min"),
- },
- },
+ Value: ast.NewCall(
+ ast.NewSel(&ast.Ident{Name: "list", Node: spec}, "Min")),
},
}},
want: "import listx \"list\", list: listx.Min()",
diff --git a/cue/build_test.go b/cue/build_test.go
index 804e7ea..912b452 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -29,12 +29,12 @@
expr ast.Expr
out string
}{{
- expr: &ast.BasicLit{Kind: token.STRING, Value: `"Hello"`},
+ expr: ast.NewString("Hello"),
out: `"Hello"`,
}, {
expr: &ast.ListLit{Elts: []ast.Expr{
- &ast.BasicLit{Kind: token.STRING, Value: `"Hello"`},
- &ast.BasicLit{Kind: token.STRING, Value: `"World"`},
+ ast.NewString("Hello"),
+ ast.NewString("World"),
}},
out: `["Hello","World"]`,
}}
diff --git a/cue/export.go b/cue/export.go
index 8035c94..9fea698 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -72,10 +72,7 @@
Expr: ast.NewIdent(info.short),
})
}
- importDecl.Specs = append(importDecl.Specs, &ast.ImportSpec{
- Name: ident,
- Path: &ast.BasicLit{Kind: token.STRING, Value: quote(k, '"')},
- })
+ importDecl.Specs = append(importDecl.Specs, ast.NewImport(ident, k))
}
if obj, ok := value.(*ast.StructLit); ok {
@@ -221,10 +218,7 @@
return s
}
if isClosed && !p.inDef {
- return &ast.CallExpr{
- Fun: ast.NewIdent("close"),
- Args: []ast.Expr{s},
- }
+ return ast.NewCall(ast.NewIdent("close"), s)
}
if !isClosed && p.inDef && !hasTemplate(s) {
s.Elts = append(s.Elts, &ast.Ellipsis{})
@@ -255,14 +249,13 @@
// TODO: also add position information.
switch x := v.(type) {
case *builtin:
- name := ast.NewIdent(x.Name)
if x.pkg == 0 {
- return name
+ return ast.NewIdent(x.Name)
}
pkg := p.ctx.labelStr(x.pkg)
inst := builtins[pkg]
short := p.shortName(inst, "", pkg)
- return &ast.SelectorExpr{X: ast.NewIdent(short), Sel: name}
+ return ast.NewSel(ast.NewIdent(short), x.Name)
case *nodeRef:
if x.short == 0 {
@@ -278,7 +271,7 @@
case *selectorExpr:
n := p.expr(x.x)
if n != nil {
- return &ast.SelectorExpr{X: n, Sel: p.identifier(x.feature)}
+ return ast.NewSel(n, p.ctx.labelStr(x.feature))
}
ident := p.identifier(x.feature)
node, ok := x.x.(*nodeRef)
@@ -338,7 +331,7 @@
return call
case *customValidator:
- call := &ast.CallExpr{Fun: p.expr(x.call)}
+ call := ast.NewCall(p.expr(x.call))
for _, a := range x.args {
call.Args = append(call.Args, p.expr(a))
}
diff --git a/cue/format/format_test.go b/cue/format/format_test.go
index ffab0f0..7c5ab8d 100644
--- a/cue/format/format_test.go
+++ b/cue/format/format_test.go
@@ -245,6 +245,7 @@
&ast.Package{Name: ast.NewIdent("foo")},
&ast.EmbedDecl{
Expr: &ast.BasicLit{
+ Kind: token.INT,
ValuePos: token.NoSpace.Pos(),
Value: "1",
},
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index dc1a230..76183c7 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -154,9 +154,7 @@
p.sorted = imported
for _, v := range imported {
- spec := &ast.ImportSpec{
- Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(v)},
- }
+ spec := ast.NewImport(nil, v)
imports.Specs = append(imports.Specs, spec)
p.file.Imports = append(p.file.Imports, spec)
}
@@ -293,13 +291,8 @@
func (p *protoConverter) toExpr(pos scanner.Position, name string) (expr ast.Expr) {
a := strings.Split(name, ".")
- for i, s := range a {
- if i == 0 {
- expr = &ast.Ident{NamePos: p.toCUEPos(pos), Name: s}
- continue
- }
- expr = &ast.SelectorExpr{X: expr, Sel: ast.NewIdent(s)}
- }
+ expr = &ast.Ident{NamePos: p.toCUEPos(pos), Name: a[0]}
+ expr = ast.NewSel(expr, a[1:]...)
return expr
}
diff --git a/internal/internal_test.go b/internal/internal_test.go
index c56ce82..a3e8e73 100644
--- a/internal/internal_test.go
+++ b/internal/internal_test.go
@@ -19,7 +19,6 @@
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
- "cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"github.com/stretchr/testify/assert"
)
@@ -30,11 +29,11 @@
out string
ok bool
}{{
- in: &ast.BasicLit{Kind: token.STRING, Value: `"foo-bar"`},
+ in: ast.NewString("foo-bar"),
out: "foo-bar",
ok: true,
}, {
- in: &ast.BasicLit{Kind: token.STRING, Value: `"foo bar"`},
+ in: ast.NewString("foo bar"),
out: "foo bar",
ok: true,
}, {