feat: struct and tuple patterns in match (bootstrap + selfhost)
Support destructuring in match arms:
- Tuple: (a, b) binds subject._0 / _1
- Struct: Point { x: px, y: py } and shorthand Point { x, y }
Bootstrap: matchPatternBindings for pkTuple/pkStruct; register local
tuple typedefs from function bodies. Selfhost: parse, Sema_BindPattern,
Lcx_PatternBindings with scope defines. Fix operator-overload path that
crashed when typeName was null after pattern binds.
Example: examples/struct_tuple_pat.bux. Selfhost-loop IDENTICAL.
This commit is contained in:
@@ -694,6 +694,7 @@ func parserMakePattern(kind: int, line: uint32, col: uint32) -> *Pattern {
|
||||
pat.patRangeInclusive = false;
|
||||
pat.patEnumPath = "";
|
||||
pat.patStructName = "";
|
||||
pat.patFieldName = "";
|
||||
pat.patChild1 = null as *Pattern;
|
||||
pat.patChild2 = null as *Pattern;
|
||||
pat.patArgs = null as *Pattern;
|
||||
@@ -767,6 +768,42 @@ func parserParsePrimaryPattern(p: *Parser) -> *Pattern {
|
||||
pat.patArgs = enumArgs;
|
||||
return pat;
|
||||
}
|
||||
// Struct pattern: Point { x: a, y: b } or shorthand Point { x, y }
|
||||
if parserCheck(p, tkLBrace) {
|
||||
discard parserAdvance(p);
|
||||
var fieldHead: *Pattern = null as *Pattern;
|
||||
var fieldTail: *Pattern = null as *Pattern;
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
while parserCheck(p, tkNewLine) { discard parserAdvance(p); }
|
||||
if parserCheck(p, tkRBrace) { break; }
|
||||
let ftok: LexToken = parserExpectIdentOrKeyword(p, "expected field name in struct pattern");
|
||||
let fieldName: String = ftok.text;
|
||||
var fieldPat: *Pattern = null as *Pattern;
|
||||
if parserCheck(p, tkColon) {
|
||||
discard parserAdvance(p);
|
||||
fieldPat = parserParsePattern(p);
|
||||
} else {
|
||||
// Shorthand { x } → { x: x }
|
||||
fieldPat = parserMakePattern(pkIdent, line, col);
|
||||
fieldPat.patIdent = fieldName;
|
||||
}
|
||||
fieldPat.patFieldName = fieldName;
|
||||
if fieldHead == null as *Pattern {
|
||||
fieldHead = fieldPat;
|
||||
fieldTail = fieldPat;
|
||||
} else {
|
||||
fieldTail.patNext = fieldPat;
|
||||
fieldTail = fieldPat;
|
||||
}
|
||||
if parserCheck(p, tkComma) { discard parserAdvance(p); }
|
||||
else { break; }
|
||||
}
|
||||
discard parserExpect(p, tkRBrace, "expected '}' to close struct pattern");
|
||||
let spat: *Pattern = parserMakePattern(pkStruct, line, col);
|
||||
spat.patStructName = name;
|
||||
spat.patArgs = fieldHead;
|
||||
return spat;
|
||||
}
|
||||
// Bare name with (args): Variant(...) treated as single-segment enum
|
||||
if parserCheck(p, tkLParen) {
|
||||
discard parserAdvance(p);
|
||||
@@ -796,6 +833,29 @@ func parserParsePrimaryPattern(p: *Parser) -> *Pattern {
|
||||
return pat;
|
||||
}
|
||||
|
||||
// Tuple pattern: (a, b)
|
||||
if kind == tkLParen {
|
||||
discard parserAdvance(p);
|
||||
var head: *Pattern = null as *Pattern;
|
||||
var tail: *Pattern = null as *Pattern;
|
||||
while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile {
|
||||
let elem: *Pattern = parserParsePattern(p);
|
||||
if head == null as *Pattern {
|
||||
head = elem;
|
||||
tail = elem;
|
||||
} else {
|
||||
tail.patNext = elem;
|
||||
tail = elem;
|
||||
}
|
||||
if parserCheck(p, tkComma) { discard parserAdvance(p); }
|
||||
else { break; }
|
||||
}
|
||||
discard parserExpect(p, tkRParen, "expected ')' to close tuple pattern");
|
||||
let tpat: *Pattern = parserMakePattern(pkTuple, line, col);
|
||||
tpat.patArgs = head;
|
||||
return tpat;
|
||||
}
|
||||
|
||||
parserEmitDiag(p, line, col, "expected pattern");
|
||||
return parserMakePattern(pkWildcard, line, col);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user