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:
@@ -217,6 +217,53 @@ proc matchPatternBindings(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
||||
result.add(hirStore(hirVar(nf.pattern.patIdent, fieldTy, loc), fieldLoad, loc))
|
||||
of pkGuarded:
|
||||
result.add(ctx.matchPatternBindings(subject, pattern.patGuardedInner, subjectEnumName, subjectHasData, loc))
|
||||
of pkTuple:
|
||||
# (a, b) => bind a = subject._0, b = subject._1
|
||||
for i, elem in pattern.patTupleElements:
|
||||
if elem == nil:
|
||||
continue
|
||||
let fieldName = "_" & $i
|
||||
let fieldTy = if subject.typ != nil and subject.typ.kind == tkTuple and i < subject.typ.inner.len:
|
||||
subject.typ.inner[i]
|
||||
else: makeInt()
|
||||
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: subject, fieldName: fieldName,
|
||||
typ: makePointer(fieldTy), loc: loc)
|
||||
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
||||
if elem.kind == pkIdent:
|
||||
if elem.patIdent notin ctx.patternBoundNames:
|
||||
result.add(hirAlloca(elem.patIdent, fieldTy, loc))
|
||||
ctx.patternBoundNames.incl(elem.patIdent)
|
||||
result.add(hirStore(hirVar(elem.patIdent, fieldTy, loc), fieldLoad, loc))
|
||||
else:
|
||||
# Nested patterns: recurse with field as subject
|
||||
result.add(ctx.matchPatternBindings(fieldLoad, elem, subjectEnumName, subjectHasData, loc))
|
||||
of pkStruct:
|
||||
# Point { x: px, y: py } => px = subject.x, py = subject.y
|
||||
var structName = pattern.patStructName
|
||||
if structName.len == 0 and subject.typ != nil and subject.typ.kind == tkNamed:
|
||||
structName = subject.typ.name
|
||||
var fieldTypes = initTable[string, Type]()
|
||||
if structName.len > 0:
|
||||
let ssym = ctx.globalScope.lookup(structName)
|
||||
if ssym != nil and ssym.decl != nil and ssym.decl.kind == dkStruct:
|
||||
for f in ssym.decl.declStructFields:
|
||||
fieldTypes[f.name] = ctx.resolveTypeExpr(f.ftype)
|
||||
for entry in pattern.patStructFields:
|
||||
let fname = entry.name
|
||||
let fpat = entry.pattern
|
||||
if fpat == nil:
|
||||
continue
|
||||
let fieldTy = if fieldTypes.hasKey(fname): fieldTypes[fname] else: makeInt()
|
||||
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: subject, fieldName: fname,
|
||||
typ: makePointer(fieldTy), loc: loc)
|
||||
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
||||
if fpat.kind == pkIdent:
|
||||
if fpat.patIdent notin ctx.patternBoundNames:
|
||||
result.add(hirAlloca(fpat.patIdent, fieldTy, loc))
|
||||
ctx.patternBoundNames.incl(fpat.patIdent)
|
||||
result.add(hirStore(hirVar(fpat.patIdent, fieldTy, loc), fieldLoad, loc))
|
||||
else:
|
||||
result.add(ctx.matchPatternBindings(fieldLoad, fpat, subjectEnumName, subjectHasData, loc))
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
@@ -767,10 +767,60 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
||||
else:
|
||||
discard
|
||||
|
||||
proc walkHirForTuples(n: HirNode) =
|
||||
if n == nil: return
|
||||
registerTuple(n.typ)
|
||||
case n.kind
|
||||
of hAlloca:
|
||||
registerTuple(n.allocaType)
|
||||
of hBlock:
|
||||
for s in n.blockStmts: walkHirForTuples(s)
|
||||
walkHirForTuples(n.blockExpr)
|
||||
of hIf:
|
||||
walkHirForTuples(n.ifCond)
|
||||
walkHirForTuples(n.ifThen)
|
||||
walkHirForTuples(n.ifElse)
|
||||
of hWhile:
|
||||
walkHirForTuples(n.whileCond)
|
||||
walkHirForTuples(n.whileBody)
|
||||
of hLoop:
|
||||
walkHirForTuples(n.loopBody)
|
||||
of hReturn:
|
||||
walkHirForTuples(n.returnValue)
|
||||
of hStore:
|
||||
walkHirForTuples(n.storePtr)
|
||||
walkHirForTuples(n.storeValue)
|
||||
of hAssign:
|
||||
walkHirForTuples(n.assignTarget)
|
||||
walkHirForTuples(n.assignValue)
|
||||
of hBinary:
|
||||
walkHirForTuples(n.binaryLeft)
|
||||
walkHirForTuples(n.binaryRight)
|
||||
of hUnary:
|
||||
walkHirForTuples(n.unaryOperand)
|
||||
of hCall:
|
||||
for a in n.callArgs: walkHirForTuples(a)
|
||||
of hCallIndirect:
|
||||
walkHirForTuples(n.callIndirectCallee)
|
||||
for a in n.callIndirectArgs: walkHirForTuples(a)
|
||||
of hLoad:
|
||||
walkHirForTuples(n.loadPtr)
|
||||
of hFieldPtr:
|
||||
walkHirForTuples(n.fieldPtrBase)
|
||||
of hFieldAccess:
|
||||
walkHirForTuples(n.fieldAccessBase)
|
||||
of hStructInit:
|
||||
for f in n.structInitFields: walkHirForTuples(f.value)
|
||||
of hTupleInit:
|
||||
for e in n.tupleInitElements: walkHirForTuples(e)
|
||||
else:
|
||||
discard
|
||||
|
||||
for f in module.funcs:
|
||||
registerTuple(f.retType)
|
||||
for p in f.params:
|
||||
registerTuple(p.typ)
|
||||
walkHirForTuples(f.body)
|
||||
for ef in module.externFuncs:
|
||||
registerTuple(ef.retType)
|
||||
for p in ef.params:
|
||||
|
||||
@@ -324,7 +324,7 @@ proc parsePrimaryPattern(p: var Parser): Pattern =
|
||||
return Pattern(kind: pkEnum, loc: loc, patEnumPath: path, patEnumArgs: args, patEnumNamed: named)
|
||||
return Pattern(kind: pkEnum, loc: loc, patEnumPath: path, patEnumArgs: @[], patEnumNamed: @[])
|
||||
elif p.check(tkLBrace):
|
||||
# Struct pattern: Point { x: 0, y: 0 }
|
||||
# Struct pattern: Point { x: px, y: py } or shorthand Point { x, y }
|
||||
discard p.advance()
|
||||
var fields: seq[tuple[name: string, pattern: Pattern]] = @[]
|
||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||
@@ -333,8 +333,12 @@ proc parsePrimaryPattern(p: var Parser): Pattern =
|
||||
if p.check(tkRBrace) or p.isAtEnd:
|
||||
break
|
||||
let fieldName = p.expect(tkIdent, "expected field name in struct pattern").text
|
||||
discard p.expect(tkColon, "expected ':' after field name in pattern")
|
||||
fields.add((fieldName, p.parsePattern()))
|
||||
if p.check(tkColon):
|
||||
discard p.advance()
|
||||
fields.add((fieldName, p.parsePattern()))
|
||||
else:
|
||||
# Shorthand: { x } means { x: x }
|
||||
fields.add((fieldName, Pattern(kind: pkIdent, loc: loc, patIdent: fieldName)))
|
||||
if p.check(tkComma):
|
||||
discard p.advance()
|
||||
discard p.expect(tkRBrace, "expected '}' to close struct pattern")
|
||||
|
||||
+17
-2
@@ -814,8 +814,23 @@ proc extractPatternBindings(sema: var Sema, pat: Pattern, scope: Scope, subjectT
|
||||
else:
|
||||
sema.extractPatternBindings(elem, scope, elemTy)
|
||||
of pkStruct:
|
||||
for f in pat.patStructFields:
|
||||
sema.extractPatternBindings(f.pattern, scope)
|
||||
# Resolve field types from struct declaration when possible
|
||||
var fieldTypes = initTable[string, Type]()
|
||||
var structName = pat.patStructName
|
||||
if structName.len == 0 and subjectType != nil and subjectType.kind == tkNamed:
|
||||
structName = subjectType.name
|
||||
if structName.len > 0:
|
||||
let ssym = sema.globalScope.lookup(structName)
|
||||
if ssym != nil and ssym.decl != nil and ssym.decl.kind == dkStruct:
|
||||
for f in ssym.decl.declStructFields:
|
||||
fieldTypes[f.name] = sema.resolveType(f.ftype)
|
||||
for entry in pat.patStructFields:
|
||||
let fty = if fieldTypes.hasKey(entry.name): fieldTypes[entry.name] else: makeUnknown()
|
||||
if entry.pattern != nil and entry.pattern.kind == pkIdent:
|
||||
let sym = Symbol(kind: skVar, name: entry.pattern.patIdent, typ: fty, isMutable: false)
|
||||
discard scope.define(sym)
|
||||
else:
|
||||
sema.extractPatternBindings(entry.pattern, scope, fty)
|
||||
of pkGuarded:
|
||||
sema.extractPatternBindings(pat.patGuardedInner, scope, subjectType)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user