cue/parser: support string selector labels
Allowed by the spec, but previously unimplemented.
Change-Id: I2fb7aad303cdfac8862a0744b80d524cb749f5b3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7342
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/ast/ast.go b/cue/ast/ast.go
index 97ea172..a7acb12 100644
--- a/cue/ast/ast.go
+++ b/cue/ast/ast.go
@@ -660,8 +660,8 @@
// A SelectorExpr node represents an expression followed by a selector.
type SelectorExpr struct {
- X Expr // expression
- Sel *Ident // field selector
+ X Expr // expression
+ Sel Label // field selector
comments
expr
diff --git a/cue/format/node.go b/cue/format/node.go
index 367003c..ebbb64f 100644
--- a/cue/format/node.go
+++ b/cue/format/node.go
@@ -909,11 +909,13 @@
f.expr1(x.X, token.HighestPrec, depth)
f.print(token.PERIOD)
if x.Sel.Pos().IsNewline() {
- f.print(indent, formfeed, x.Sel.Pos(), x.Sel)
+ f.print(indent, formfeed)
+ f.expr(x.Sel.(ast.Expr))
f.print(unindent)
return true
}
- f.print(x.Sel.Pos(), x.Sel)
+ f.print(noblank)
+ f.expr(x.Sel.(ast.Expr))
return false
}
diff --git a/cue/format/testdata/expressions.golden b/cue/format/testdata/expressions.golden
index be50d3d..b7e4457 100644
--- a/cue/format/testdata/expressions.golden
+++ b/cue/format/testdata/expressions.golden
@@ -213,4 +213,14 @@
]
foo: bar
+
+ a: "foo-bar": 3
+ b: a."foo-bar"
+ c: a."foo-bar".b
+ d: a.
+ "foo-bar"
+ e: a.
+ "foo-bar".
+ b
+ f: 2
}
diff --git a/cue/format/testdata/expressions.input b/cue/format/testdata/expressions.input
index a1be441..c0898ee 100644
--- a/cue/format/testdata/expressions.input
+++ b/cue/format/testdata/expressions.input
@@ -209,4 +209,14 @@
]
foo : bar
+
+ a: "foo-bar": 3
+ b: a."foo-bar"
+ c: a. "foo-bar" . b
+ d: a.
+ "foo-bar"
+ e: a.
+ "foo-bar".
+ b
+ f: 2
}
\ No newline at end of file
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index b0b1431..dbdf0f3 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -1361,6 +1361,21 @@
X: p.checkExpr(x),
Sel: p.parseIdent(),
}
+ case token.STRING:
+ if strings.HasPrefix(p.lit, `"`) && !strings.HasPrefix(p.lit, `""`) {
+ str := &ast.BasicLit{
+ ValuePos: p.pos,
+ Kind: token.STRING,
+ Value: p.lit,
+ }
+ p.next()
+ x = &ast.SelectorExpr{
+ X: p.checkExpr(x),
+ Sel: str,
+ }
+ break
+ }
+ fallthrough
default:
pos := p.pos
p.errorExpected(pos, "selector")
diff --git a/cue/parser/parser_test.go b/cue/parser/parser_test.go
index a829192..42c9c8e 100644
--- a/cue/parser/parser_test.go
+++ b/cue/parser/parser_test.go
@@ -117,6 +117,23 @@
`{ V1, V2 }`,
`{V1, V2}`,
}, {
+ "selectors",
+ `a.b. "str"`,
+ `a.b."str"`,
+ }, {
+ "selectors",
+ `a.b. "str"`,
+ `a.b."str"`,
+ }, {
+ "faulty bytes selector",
+ `a.b.'str'`,
+ "a.b._\nexpected selector, found 'STRING' 'str'",
+ }, {
+ "faulty multiline string selector",
+ `a.b."""
+ """`,
+ "a.b._\nexpected selector, found 'STRING' \"\"\"\n\t\t\t\"\"\"",
+ }, {
"expression embedding",
`#Def: {
a.b.c
@@ -697,8 +714,8 @@
// *BadExpr.
func TestIncompleteSelection(t *testing.T) {
for _, src := range []string{
- "{ a: fmt. }", // at end of object
- "{ a: fmt.\n\"a\": x }", // not at end of struct
+ "{ a: fmt. }", // at end of object
+ "{ a: fmt.\n0.0: x }", // not at end of struct
} {
t.Run("", func(t *testing.T) {
f, err := ParseFile("", src)
diff --git a/cue/testdata/eval/selectors.txtar b/cue/testdata/eval/selectors.txtar
index 08be88e..4fdc64e 100644
--- a/cue/testdata/eval/selectors.txtar
+++ b/cue/testdata/eval/selectors.txtar
@@ -1,14 +1,21 @@
-- in.cue --
- a: 1
- b: a + 1
- d: {
- x: _
- y: b + x
- }
- e: d & {
- x: 5
- }
-
+a: 1
+b: a + 1
+d: {
+ x: _
+ y: b + x
+}
+e: d & {
+ x: 5
+}
+f: {
+ a: "foo-bar": 3
+ b: a."foo-bar"
+}
+g: {
+ a: "foo-bar": c: 3
+ b: a."foo-bar".c
+}
-- out/eval --
(struct){
a: (int){ 1 }
@@ -24,6 +31,20 @@
x: (int){ 5 }
y: (int){ 7 }
}
+ f: (struct){
+ a: (struct){
+ "foo-bar": (int){ 3 }
+ }
+ b: (int){ 3 }
+ }
+ g: (struct){
+ a: (struct){
+ "foo-bar": (struct){
+ c: (int){ 3 }
+ }
+ }
+ b: (int){ 3 }
+ }
}
-- out/compile --
--- in.cue
@@ -37,4 +58,18 @@
e: (〈0;d〉 & {
x: 5
})
+ f: {
+ a: {
+ "foo-bar": 3
+ }
+ b: 〈0;a〉."foo-bar"
+ }
+ g: {
+ a: {
+ "foo-bar": {
+ c: 3
+ }
+ }
+ b: 〈0;a〉."foo-bar".c
+ }
}
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 5ad3380..131c3b3 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -583,7 +583,7 @@
func (x *SelectorExpr) resolve(c *OpContext) *Vertex {
n := c.node(x.X, Partial)
- return c.lookup(n, x.Src.Sel.NamePos, x.Sel)
+ return c.lookup(n, x.Src.Sel.Pos(), x.Sel)
}
// IndexExpr is like a selector, but selects an index.
diff --git a/internal/core/export/adt.go b/internal/core/export/adt.go
index bff96e7..8013a05 100644
--- a/internal/core/export/adt.go
+++ b/internal/core/export/adt.go
@@ -155,7 +155,7 @@
case *adt.SelectorExpr:
return &ast.SelectorExpr{
X: e.expr(x.X),
- Sel: e.ident(x.Sel),
+ Sel: e.stringLabel(x.Sel),
}
case *adt.IndexExpr: