Skip to content

Commit 18367a6

Browse files
haoqixumvdan
andcommitted
cue/parser: allow if as a label for a required field
The field `if!: 123` resulted in a parse error because the token `if` followed by `!` was understood as the start of a comprehension with a negated expression, like: if !boolexpr { ... } To differentiate the two cases, we need to check if the following token is ':', meaning that we are declaring a required field. Teach the parser how to peek one token past the current one. As a reminder, the CUE language allows using keywords as labels. All other keywords already worked and had tests, including for required fields; only `if` was broken for the reason outlined above. Change-Id: I1ea9f3b82499b9c9b5e896c1d3857be86d7ca2b8 Signed-off-by: haoqixu <[email protected]> Co-authored-by: Daniel Martí <[email protected]> Signed-off-by: Daniel Martí <[email protected]> Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1209287 TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Paul Jolly <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent cdc4123 commit 18367a6

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

cue/parser/parser.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,20 @@ type parser struct {
4343
leadComment *ast.CommentGroup
4444
comments *commentState
4545

46-
// Next token
46+
// Next token, filled by [parser.next0].
4747
pos token.Pos // token position
4848
tok token.Token // one token look-ahead
4949
lit string // token literal
5050

51+
// Token after next, filled by [parser.peek].
52+
peekToken struct {
53+
scanned bool
54+
55+
pos token.Pos
56+
tok token.Token
57+
lit string
58+
}
59+
5160
// Error recovery
5261
// (used to limit the number of calls to sync... functions
5362
// w/o making scanning progress - avoids potential endless
@@ -269,9 +278,26 @@ func (p *parser) next0() {
269278
}
270279
}
271280

281+
// We had peeked one token, effectively scanning it early; use it now.
282+
if p.peekToken.scanned {
283+
p.pos, p.tok, p.lit = p.peekToken.pos, p.peekToken.tok, p.peekToken.lit
284+
p.peekToken.scanned = false
285+
return
286+
}
287+
272288
p.pos, p.tok, p.lit = p.scanner.Scan()
273289
}
274290

291+
// peek scans one more token as a look-ahead and stores it in [parser.peekToken].
292+
// Peeking multiple tokens ahead is not supported.
293+
func (p *parser) peek() {
294+
if p.peekToken.scanned {
295+
panic("can only peek one token at a time")
296+
}
297+
p.peekToken.pos, p.peekToken.tok, p.peekToken.lit = p.scanner.Scan()
298+
p.peekToken.scanned = true
299+
}
300+
275301
// Consume a comment and return it and the line on which it ends.
276302
func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
277303
endline = p.file.Line(p.pos)
@@ -1116,6 +1142,11 @@ func (p *parser) parseComprehensionClauses(first bool) (clauses []ast.Clause, c
11161142
case token.COLON, token.BIND, token.OPTION,
11171143
token.COMMA, token.EOF:
11181144
return nil, c
1145+
case token.NOT:
1146+
p.peek()
1147+
if p.peekToken.tok == token.COLON {
1148+
return nil, c
1149+
}
11191150
}
11201151
}
11211152

cue/parser/parser_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ func TestParse(t *testing.T) {
8080
`if?: 0, for?: 1, in?: 2, where?: 3, div?: 4, quo?: 5, func?: 6, for?: {if?: {func?: {let?: 3}}}`,
8181
}, {
8282
"keywords as required labels",
83-
`for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6
83+
`if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6
8484
for!: if!: func!: let!: 3
8585
`,
86-
`for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6, for!: {if!: {func!: {let!: 3}}}`,
86+
`if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6, for!: {if!: {func!: {let!: 3}}}`,
8787
}, {
8888
"keywords as alias",
8989
`if=foo: 0

0 commit comments

Comments
 (0)