Skip to content

Commit 732b6d5

Browse files
committed
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 <[email protected]> Reviewed-by: CUE cueckoo <[email protected]>
1 parent 3914ef8 commit 732b6d5

File tree

9 files changed

+107
-18
lines changed

9 files changed

+107
-18
lines changed

cue/ast/ast.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,8 @@ type ParenExpr struct {
660660

661661
// A SelectorExpr node represents an expression followed by a selector.
662662
type SelectorExpr struct {
663-
X Expr // expression
664-
Sel *Ident // field selector
663+
X Expr // expression
664+
Sel Label // field selector
665665

666666
comments
667667
expr

cue/format/node.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,11 +909,13 @@ func (f *formatter) selectorExpr(x *ast.SelectorExpr, depth int) bool {
909909
f.expr1(x.X, token.HighestPrec, depth)
910910
f.print(token.PERIOD)
911911
if x.Sel.Pos().IsNewline() {
912-
f.print(indent, formfeed, x.Sel.Pos(), x.Sel)
912+
f.print(indent, formfeed)
913+
f.expr(x.Sel.(ast.Expr))
913914
f.print(unindent)
914915
return true
915916
}
916-
f.print(x.Sel.Pos(), x.Sel)
917+
f.print(noblank)
918+
f.expr(x.Sel.(ast.Expr))
917919
return false
918920
}
919921

cue/format/testdata/expressions.golden

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,14 @@ package expressions
213213
]
214214

215215
foo: bar
216+
217+
a: "foo-bar": 3
218+
b: a."foo-bar"
219+
c: a."foo-bar".b
220+
d: a.
221+
"foo-bar"
222+
e: a.
223+
"foo-bar".
224+
b
225+
f: 2
216226
}

cue/format/testdata/expressions.input

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,14 @@ package expressions
209209
]
210210

211211
foo : bar
212+
213+
a: "foo-bar": 3
214+
b: a."foo-bar"
215+
c: a. "foo-bar" . b
216+
d: a.
217+
"foo-bar"
218+
e: a.
219+
"foo-bar".
220+
b
221+
f: 2
212222
}

cue/parser/parser.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,21 @@ L:
13611361
X: p.checkExpr(x),
13621362
Sel: p.parseIdent(),
13631363
}
1364+
case token.STRING:
1365+
if strings.HasPrefix(p.lit, `"`) && !strings.HasPrefix(p.lit, `""`) {
1366+
str := &ast.BasicLit{
1367+
ValuePos: p.pos,
1368+
Kind: token.STRING,
1369+
Value: p.lit,
1370+
}
1371+
p.next()
1372+
x = &ast.SelectorExpr{
1373+
X: p.checkExpr(x),
1374+
Sel: str,
1375+
}
1376+
break
1377+
}
1378+
fallthrough
13641379
default:
13651380
pos := p.pos
13661381
p.errorExpected(pos, "selector")

cue/parser/parser_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,23 @@ func TestParse(t *testing.T) {
116116
"one-line embedding",
117117
`{ V1, V2 }`,
118118
`{V1, V2}`,
119+
}, {
120+
"selectors",
121+
`a.b. "str"`,
122+
`a.b."str"`,
123+
}, {
124+
"selectors",
125+
`a.b. "str"`,
126+
`a.b."str"`,
127+
}, {
128+
"faulty bytes selector",
129+
`a.b.'str'`,
130+
"a.b._\nexpected selector, found 'STRING' 'str'",
131+
}, {
132+
"faulty multiline string selector",
133+
`a.b."""
134+
"""`,
135+
"a.b._\nexpected selector, found 'STRING' \"\"\"\n\t\t\t\"\"\"",
119136
}, {
120137
"expression embedding",
121138
`#Def: {
@@ -697,8 +714,8 @@ func TestImports(t *testing.T) {
697714
// *BadExpr.
698715
func TestIncompleteSelection(t *testing.T) {
699716
for _, src := range []string{
700-
"{ a: fmt. }", // at end of object
701-
"{ a: fmt.\n\"a\": x }", // not at end of struct
717+
"{ a: fmt. }", // at end of object
718+
"{ a: fmt.\n0.0: x }", // not at end of struct
702719
} {
703720
t.Run("", func(t *testing.T) {
704721
f, err := ParseFile("", src)

cue/testdata/eval/selectors.txtar

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
-- in.cue --
2-
a: 1
3-
b: a + 1
4-
d: {
5-
x: _
6-
y: b + x
7-
}
8-
e: d & {
9-
x: 5
10-
}
11-
2+
a: 1
3+
b: a + 1
4+
d: {
5+
x: _
6+
y: b + x
7+
}
8+
e: d & {
9+
x: 5
10+
}
11+
f: {
12+
a: "foo-bar": 3
13+
b: a."foo-bar"
14+
}
15+
g: {
16+
a: "foo-bar": c: 3
17+
b: a."foo-bar".c
18+
}
1219
-- out/eval --
1320
(struct){
1421
a: (int){ 1 }
@@ -24,6 +31,20 @@
2431
x: (int){ 5 }
2532
y: (int){ 7 }
2633
}
34+
f: (struct){
35+
a: (struct){
36+
"foo-bar": (int){ 3 }
37+
}
38+
b: (int){ 3 }
39+
}
40+
g: (struct){
41+
a: (struct){
42+
"foo-bar": (struct){
43+
c: (int){ 3 }
44+
}
45+
}
46+
b: (int){ 3 }
47+
}
2748
}
2849
-- out/compile --
2950
--- in.cue
@@ -37,4 +58,18 @@
3758
e: (〈0;d〉 & {
3859
x: 5
3960
})
61+
f: {
62+
a: {
63+
"foo-bar": 3
64+
}
65+
b: 〈0;a〉."foo-bar"
66+
}
67+
g: {
68+
a: {
69+
"foo-bar": {
70+
c: 3
71+
}
72+
}
73+
b: 〈0;a〉."foo-bar".c
74+
}
4075
}

internal/core/adt/expr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ func (x *SelectorExpr) Source() ast.Node {
583583

584584
func (x *SelectorExpr) resolve(c *OpContext) *Vertex {
585585
n := c.node(x.X, Partial)
586-
return c.lookup(n, x.Src.Sel.NamePos, x.Sel)
586+
return c.lookup(n, x.Src.Sel.Pos(), x.Sel)
587587
}
588588

589589
// IndexExpr is like a selector, but selects an index.

internal/core/export/adt.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func (e *exporter) adt(expr adt.Expr, conjuncts []adt.Conjunct) ast.Expr {
155155
case *adt.SelectorExpr:
156156
return &ast.SelectorExpr{
157157
X: e.expr(x.X),
158-
Sel: e.ident(x.Sel),
158+
Sel: e.stringLabel(x.Sel),
159159
}
160160

161161
case *adt.IndexExpr:

0 commit comments

Comments
 (0)