parser: parenthesized match-arm value vs payload capture
A match arm `case PAT: (expr)` — e.g. `case 0: (5)` — failed to parse:
parseMatchBody unconditionally consumed an `(` after `case PAT:` as a
payload-capture `(ident)`, so a non-identifier first token produced
"expected capture name".
Disambiguate: treat `(` as a capture only when it encloses exactly a lone
identifier — `( ident )` — via a new isLoneIdentParen() helper (peekTag-based
two-token lookahead). Otherwise the parens belong to the arm-body expression.
Payload capture (`case .b: (v) { ... }`, examples/128) still binds.
This fixes the scalar paren arm value (`case 0: (5)` now parses and runs).
The tuple arm-value form (`case .X: (a, b)`) additionally needs a tuple
literal in statement/binding position, tracked separately as issue 0059.
Tests: two inline parser unit tests (paren arm value is not a capture; lone
`(ident)` still binds). Gates: zig build, zig build test, 273/273 examples.
This commit is contained in:
@@ -1270,6 +1270,13 @@ pub const Parser = struct {
|
||||
return tok.tag;
|
||||
}
|
||||
|
||||
/// With `self.current` at `(`, true iff the parens enclose exactly a single
|
||||
/// identifier — `( ident )`. Distinguishes a match-arm payload capture from
|
||||
/// a parenthesized / tuple arm-value expression (`(5)`, `(a, b)`).
|
||||
fn isLoneIdentParen(self: *Parser) bool {
|
||||
return self.peekTag(1) == .identifier and self.peekTag(2) == .r_paren;
|
||||
}
|
||||
|
||||
fn foreignRuntimeForOffset(self: *Parser, offset: usize) ?ast.ForeignRuntime {
|
||||
const tag = self.peekTag(offset);
|
||||
return switch (tag) {
|
||||
@@ -3093,13 +3100,15 @@ pub const Parser = struct {
|
||||
} else try self.parsePrimary(); // .variant
|
||||
try self.expect(.colon);
|
||||
|
||||
// Optional payload capture: (ident)
|
||||
// Optional payload capture: `(ident)`. Disambiguated from a
|
||||
// parenthesized / tuple arm-value expression (`(5)`, `(1, 1)`):
|
||||
// a capture is exactly `( <identifier> )`; anything else is the
|
||||
// arm body (an expression) and is left for the body parse below.
|
||||
var capture: ?[]const u8 = null;
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance();
|
||||
if (self.current.tag != .identifier) return self.fail("expected capture name");
|
||||
if (self.current.tag == .l_paren and self.isLoneIdentParen()) {
|
||||
self.advance(); // '('
|
||||
capture = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
self.advance(); // ident
|
||||
try self.expect(.r_paren);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user