feat: nested multi-field enum patterns (bootstrap + selfhost)
Multi-field variants use Enum_Variant_Payload nested types (avoids tag name clash). Sema resolves data.Variant.Variant_i; enum field types fully resolve tuples. Selfhost parses full type exprs in enum payloads; emit structs/tuples before enums. Example: examples/nested_patterns.bux (Pair::Two, Box::Val((a,c)), Shape::Dot).
This commit is contained in:
@@ -3,7 +3,7 @@ SRC := bootstrap/main.nim
|
|||||||
OUT := buxc
|
OUT := buxc
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
|
|
||||||
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic struct_tuple_pat match_block
|
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic struct_tuple_pat match_block nested_patterns
|
||||||
|
|
||||||
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp
|
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp
|
||||||
|
|
||||||
|
|||||||
+20
-19
@@ -600,23 +600,12 @@ proc emitEnum*(be: var CBackend, name: string, variants: seq[HirEnumVariant]) =
|
|||||||
let typ = typeToC(be, v.fields[0])
|
let typ = typeToC(be, v.fields[0])
|
||||||
be.emitLine(&"{typ} {v.name}_0;")
|
be.emitLine(&"{typ} {v.name}_0;")
|
||||||
elif v.fields.len > 1:
|
elif v.fields.len > 1:
|
||||||
# Multi positional fields — nested struct so fields don't overlay
|
# Multi positional — named nested typedef Enum_Variant_Payload
|
||||||
be.emitLine(&"struct {{")
|
let nestedName = name & "_" & v.name & "_Payload"
|
||||||
inc be.indent
|
be.emitLine(&"{nestedName} {v.name};")
|
||||||
for i, f in v.fields:
|
|
||||||
let typ = typeToC(be, f)
|
|
||||||
be.emitLine(&"{typ} {v.name}_{i};")
|
|
||||||
dec be.indent
|
|
||||||
be.emitLine(&"}} {v.name};")
|
|
||||||
elif v.namedFields.len > 0:
|
elif v.namedFields.len > 0:
|
||||||
# Named fields - generate as struct
|
let nestedName = name & "_" & v.name & "_Payload"
|
||||||
be.emitLine(&"struct {{")
|
be.emitLine(&"{nestedName} {v.name};")
|
||||||
inc be.indent
|
|
||||||
for nf in v.namedFields:
|
|
||||||
let typ = typeToC(be, nf.typ)
|
|
||||||
be.emitLine(&"{typ} {nf.name};")
|
|
||||||
dec be.indent
|
|
||||||
be.emitLine(&"}} {v.name};")
|
|
||||||
dec be.indent
|
dec be.indent
|
||||||
be.emitLine(&"}} {name}_Data;")
|
be.emitLine(&"}} {name}_Data;")
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
@@ -716,15 +705,27 @@ proc emitModule*(be: var CBackend, module: HirModule): string =
|
|||||||
if module.structs.len > 0:
|
if module.structs.len > 0:
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
|
|
||||||
# Enum definitions (must come before structs that reference them)
|
# Nested multi-field enum payloads (Enum_Variant_Payload) must be fully
|
||||||
|
# defined before the algebraic enum union that embeds them by value.
|
||||||
|
var payloadStructNames: seq[string] = @[]
|
||||||
|
for e in module.enums:
|
||||||
|
for v in e.variants:
|
||||||
|
if v.fields.len > 1 or v.namedFields.len > 0:
|
||||||
|
payloadStructNames.add(e.name & "_" & v.name & "_Payload")
|
||||||
|
for s in module.structs:
|
||||||
|
if s.name in payloadStructNames:
|
||||||
|
be.emitStruct(s.name, s.fields)
|
||||||
|
|
||||||
|
# Enum definitions (after payload structs; before user structs that may use them)
|
||||||
for e in module.enums:
|
for e in module.enums:
|
||||||
be.emitEnum(e.name, e.variants)
|
be.emitEnum(e.name, e.variants)
|
||||||
if module.enums.len > 0:
|
if module.enums.len > 0:
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
|
|
||||||
# Struct definitions
|
# Remaining struct definitions (skip payloads already emitted)
|
||||||
for s in module.structs:
|
for s in module.structs:
|
||||||
be.emitStruct(s.name, s.fields)
|
if s.name notin payloadStructNames:
|
||||||
|
be.emitStruct(s.name, s.fields)
|
||||||
|
|
||||||
# Slice fat-pointer typedefs
|
# Slice fat-pointer typedefs
|
||||||
if sliceTypes.len > 0:
|
if sliceTypes.len > 0:
|
||||||
|
|||||||
+25
-25
@@ -180,7 +180,9 @@ proc matchPatternBindings(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
|||||||
let multiField = fieldTypes.len > 1
|
let multiField = fieldTypes.len > 1
|
||||||
var payloadBase = dataLoad
|
var payloadBase = dataLoad
|
||||||
if multiField:
|
if multiField:
|
||||||
let variantStructTy = makeNamed(variantName)
|
# Nested struct type Enum_Variant_Payload (avoids clash with tag Enum_Variant)
|
||||||
|
let nestedName = enumName & "_" & variantName & "_Payload"
|
||||||
|
let variantStructTy = makeNamed(nestedName)
|
||||||
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
||||||
typ: makePointer(variantStructTy), loc: loc)
|
typ: makePointer(variantStructTy), loc: loc)
|
||||||
payloadBase = HirNode(kind: hLoad, loadPtr: variantPtr, typ: variantStructTy, loc: loc)
|
payloadBase = HirNode(kind: hLoad, loadPtr: variantPtr, typ: variantStructTy, loc: loc)
|
||||||
@@ -208,10 +210,12 @@ proc matchPatternBindings(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
|||||||
if entry.name == nf.name:
|
if entry.name == nf.name:
|
||||||
fieldTy = entry.typ
|
fieldTy = entry.typ
|
||||||
break
|
break
|
||||||
# Named payload fields live under data.VariantName.name
|
# Named payload fields live under data.Variant.name on Enum_Variant_Payload
|
||||||
|
let nestedName = enumName & "_" & variantName & "_Payload"
|
||||||
|
let variantStructTy = makeNamed(nestedName)
|
||||||
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
||||||
typ: makePointer(makeNamed(variantName)), loc: loc)
|
typ: makePointer(variantStructTy), loc: loc)
|
||||||
let variantLoad = HirNode(kind: hLoad, loadPtr: variantPtr, typ: makeNamed(variantName), loc: loc)
|
let variantLoad = HirNode(kind: hLoad, loadPtr: variantPtr, typ: variantStructTy, loc: loc)
|
||||||
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: variantLoad, fieldName: nf.name,
|
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: variantLoad, fieldName: nf.name,
|
||||||
typ: makePointer(fieldTy), loc: loc)
|
typ: makePointer(fieldTy), loc: loc)
|
||||||
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
||||||
@@ -2217,33 +2221,29 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
for v in decl.declEnumVariants:
|
for v in decl.declEnumVariants:
|
||||||
var fields: seq[Type] = @[]
|
var fields: seq[Type] = @[]
|
||||||
for f in v.fields:
|
for f in v.fields:
|
||||||
var fType = makeUnknown()
|
# Full resolve — supports tuples, pointers, named types, etc.
|
||||||
if f != nil and f.kind == tekNamed:
|
fields.add(if f != nil: ctx.resolveTypeExpr(f) else: makeUnknown())
|
||||||
case f.typeName
|
|
||||||
of "int", "int32": fType = makeInt()
|
|
||||||
of "int64": fType = makeInt64()
|
|
||||||
of "float64": fType = makeFloat64()
|
|
||||||
of "float32": fType = makeFloat32()
|
|
||||||
of "bool": fType = makeBool()
|
|
||||||
of "String", "str": fType = makeStr()
|
|
||||||
else: fType = makeNamed(f.typeName)
|
|
||||||
fields.add(fType)
|
|
||||||
|
|
||||||
var namedFields: seq[tuple[name: string, typ: Type]] = @[]
|
var namedFields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
for nf in v.namedFields:
|
for nf in v.namedFields:
|
||||||
var fType = makeUnknown()
|
let fType = if nf.ftype != nil: ctx.resolveTypeExpr(nf.ftype) else: makeUnknown()
|
||||||
if nf.ftype != nil and nf.ftype.kind == tekNamed:
|
|
||||||
case nf.ftype.typeName
|
|
||||||
of "int", "int32": fType = makeInt()
|
|
||||||
of "int64": fType = makeInt64()
|
|
||||||
of "float64": fType = makeFloat64()
|
|
||||||
of "float32": fType = makeFloat32()
|
|
||||||
of "bool": fType = makeBool()
|
|
||||||
of "String", "str": fType = makeStr()
|
|
||||||
else: fType = makeNamed(nf.ftype.typeName)
|
|
||||||
namedFields.add((nf.name, fType))
|
namedFields.add((nf.name, fType))
|
||||||
|
|
||||||
variants.add(HirEnumVariant(name: v.name, fields: fields, namedFields: namedFields))
|
variants.add(HirEnumVariant(name: v.name, fields: fields, namedFields: namedFields))
|
||||||
|
# Multi-field / named-field variants get a named nested struct type
|
||||||
|
# Enum_Variant_Payload (suffix avoids clash with tag constant Enum_Variant).
|
||||||
|
if fields.len > 1:
|
||||||
|
var nestedFields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
|
for i, ft in fields:
|
||||||
|
nestedFields.add((v.name & "_" & $i, ft))
|
||||||
|
let nestedName = decl.declEnumName & "_" & v.name & "_Payload"
|
||||||
|
structs.add((nestedName, nestedFields))
|
||||||
|
elif namedFields.len > 0:
|
||||||
|
var nestedFields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
|
for nf in namedFields:
|
||||||
|
nestedFields.add((nf.name, nf.typ))
|
||||||
|
let nestedName = decl.declEnumName & "_" & v.name & "_Payload"
|
||||||
|
structs.add((nestedName, nestedFields))
|
||||||
enums.add((decl.declEnumName, variants))
|
enums.add((decl.declEnumName, variants))
|
||||||
of dkConst:
|
of dkConst:
|
||||||
let value = ctx.lowerExpr(decl.declConstValue)
|
let value = ctx.lowerExpr(decl.declConstValue)
|
||||||
|
|||||||
+107
-111
@@ -500,20 +500,12 @@ proc emitEnumDef(be: var LirCBackend, name: string, variants: seq[HirEnumVariant
|
|||||||
# Single positional field — flat (compat: data.Variant_0)
|
# Single positional field — flat (compat: data.Variant_0)
|
||||||
be.emitLine(&"{typeToCStr(v.fields[0])} {v.name}_0;")
|
be.emitLine(&"{typeToCStr(v.fields[0])} {v.name}_0;")
|
||||||
elif v.fields.len > 1:
|
elif v.fields.len > 1:
|
||||||
# Multi positional — nested struct so fields don't share union storage
|
# Multi positional — named nested struct Enum_Variant_Payload
|
||||||
be.emitLine(&"struct {{")
|
let nestedName = name & "_" & v.name & "_Payload"
|
||||||
be.indent += 1
|
be.emitLine(&"{nestedName} {v.name};")
|
||||||
for i, f in v.fields:
|
|
||||||
be.emitLine(&"{typeToCStr(f)} {v.name}_{i};")
|
|
||||||
be.indent -= 1
|
|
||||||
be.emitLine(&"}} {v.name};")
|
|
||||||
elif v.namedFields.len > 0:
|
elif v.namedFields.len > 0:
|
||||||
be.emitLine(&"struct {{")
|
let nestedName = name & "_" & v.name & "_Payload"
|
||||||
be.indent += 1
|
be.emitLine(&"{nestedName} {v.name};")
|
||||||
for nf in v.namedFields:
|
|
||||||
be.emitLine(&"{typeToCStr(nf.typ)} {nf.name};")
|
|
||||||
be.indent -= 1
|
|
||||||
be.emitLine(&"}} {v.name};")
|
|
||||||
be.indent -= 1
|
be.indent -= 1
|
||||||
be.emitLine(&"}} {name}_Data;")
|
be.emitLine(&"}} {name}_Data;")
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
@@ -648,104 +640,8 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
for e in module.enums:
|
for e in module.enums:
|
||||||
localTypeNames.incl(e.name)
|
localTypeNames.incl(e.name)
|
||||||
|
|
||||||
# Collect slice types used in struct fields and enum payloads.
|
# Emit tuple typedefs early — enums/structs may embed them by value
|
||||||
var sliceTypes: seq[tuple[name: string, elem: string]] = @[]
|
# (e.g. Box::Val((int,int)) → Tuple_int_int Val_0 in the union).
|
||||||
var sliceNames: HashSet[string]
|
|
||||||
proc registerSlice(t: Type) =
|
|
||||||
if t == nil or t.kind != tkSlice: return
|
|
||||||
let name = typeToCStr(t)
|
|
||||||
if sliceNames.contains(name): return
|
|
||||||
sliceNames.incl(name)
|
|
||||||
let elem = if t.inner.len > 0: typeToCStr(t.inner[0]) else: "void"
|
|
||||||
sliceTypes.add((name, elem))
|
|
||||||
|
|
||||||
for s in module.structs:
|
|
||||||
for f in s.fields:
|
|
||||||
registerSlice(f.typ)
|
|
||||||
for e in module.enums:
|
|
||||||
for v in e.variants:
|
|
||||||
for ft in v.fields:
|
|
||||||
registerSlice(ft)
|
|
||||||
for nf in v.namedFields:
|
|
||||||
registerSlice(nf.typ)
|
|
||||||
|
|
||||||
# Build dependency graph among structs, enums, and slice types.
|
|
||||||
# Edge A -> B means "A depends on B, so B must be emitted before A".
|
|
||||||
var deps: Table[string, seq[string]]
|
|
||||||
for s in module.structs:
|
|
||||||
deps[s.name] = @[]
|
|
||||||
for e in module.enums:
|
|
||||||
deps[e.name] = @[]
|
|
||||||
for st in sliceTypes:
|
|
||||||
deps[st.name] = @[]
|
|
||||||
|
|
||||||
proc addDeps(node: string, t: Type) =
|
|
||||||
for dep in collectValueDeps(t):
|
|
||||||
if dep == node: continue
|
|
||||||
if localTypeNames.contains(dep) or sliceNames.contains(dep):
|
|
||||||
if dep notin deps[node]:
|
|
||||||
deps[node].add(dep)
|
|
||||||
|
|
||||||
for s in module.structs:
|
|
||||||
for f in s.fields:
|
|
||||||
addDeps(s.name, f.typ)
|
|
||||||
for e in module.enums:
|
|
||||||
for v in e.variants:
|
|
||||||
for ft in v.fields:
|
|
||||||
addDeps(e.name, ft)
|
|
||||||
for nf in v.namedFields:
|
|
||||||
addDeps(e.name, nf.typ)
|
|
||||||
|
|
||||||
# Topological sort (Kahn's algorithm).
|
|
||||||
var inDegree: Table[string, int]
|
|
||||||
var dependents: Table[string, seq[string]]
|
|
||||||
for node in deps.keys:
|
|
||||||
inDegree[node] = 0
|
|
||||||
for node, nodeDeps in deps:
|
|
||||||
for d in nodeDeps:
|
|
||||||
if not inDegree.hasKey(d): inDegree[d] = 0
|
|
||||||
inDegree[node] += 1
|
|
||||||
dependents.mgetOrPut(d, @[]).add(node)
|
|
||||||
|
|
||||||
var queue: seq[string] = @[]
|
|
||||||
for node, deg in inDegree:
|
|
||||||
if deg == 0:
|
|
||||||
queue.add(node)
|
|
||||||
|
|
||||||
var sorted: seq[string] = @[]
|
|
||||||
while queue.len > 0:
|
|
||||||
let node = queue.pop()
|
|
||||||
sorted.add(node)
|
|
||||||
for depNode in dependents.getOrDefault(node):
|
|
||||||
inDegree[depNode] -= 1
|
|
||||||
if inDegree[depNode] == 0:
|
|
||||||
queue.add(depNode)
|
|
||||||
|
|
||||||
if sorted.len < deps.len:
|
|
||||||
# Cycle detected; fall back to a safe deterministic order.
|
|
||||||
sorted = @[]
|
|
||||||
for s in module.structs: sorted.add(s.name)
|
|
||||||
for e in module.enums: sorted.add(e.name)
|
|
||||||
for st in sliceTypes: sorted.add(st.name)
|
|
||||||
|
|
||||||
# Map type names back to their definitions.
|
|
||||||
var structMap: Table[string, seq[tuple[name: string, typ: Type]]]
|
|
||||||
for s in module.structs: structMap[s.name] = s.fields
|
|
||||||
var enumMap: Table[string, seq[HirEnumVariant]]
|
|
||||||
for e in module.enums: enumMap[e.name] = e.variants
|
|
||||||
var sliceMap: Table[string, string]
|
|
||||||
for st in sliceTypes: sliceMap[st.name] = st.elem
|
|
||||||
|
|
||||||
# Emit type definitions in dependency order.
|
|
||||||
for name in sorted:
|
|
||||||
if structMap.hasKey(name):
|
|
||||||
be.emitStructDef(name, structMap[name])
|
|
||||||
elif enumMap.hasKey(name):
|
|
||||||
be.emitEnumDef(name, enumMap[name])
|
|
||||||
elif sliceMap.hasKey(name):
|
|
||||||
be.emitSliceTypeDef(name, sliceMap[name])
|
|
||||||
|
|
||||||
# Collect and emit tuple typedefs used in the module (and nested tuples first).
|
|
||||||
var tupleTypes: seq[Type] = @[]
|
var tupleTypes: seq[Type] = @[]
|
||||||
var tupleNames: HashSet[string]
|
var tupleNames: HashSet[string]
|
||||||
proc registerTuple(t: Type) =
|
proc registerTuple(t: Type) =
|
||||||
@@ -840,6 +736,106 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
for tt in tupleTypes:
|
for tt in tupleTypes:
|
||||||
be.emitTupleDef(tt)
|
be.emitTupleDef(tt)
|
||||||
|
|
||||||
|
# Collect slice types used in struct fields and enum payloads.
|
||||||
|
var sliceTypes: seq[tuple[name: string, elem: string]] = @[]
|
||||||
|
var sliceNames: HashSet[string]
|
||||||
|
proc registerSlice(t: Type) =
|
||||||
|
if t == nil or t.kind != tkSlice: return
|
||||||
|
let name = typeToCStr(t)
|
||||||
|
if sliceNames.contains(name): return
|
||||||
|
sliceNames.incl(name)
|
||||||
|
let elem = if t.inner.len > 0: typeToCStr(t.inner[0]) else: "void"
|
||||||
|
sliceTypes.add((name, elem))
|
||||||
|
|
||||||
|
for s in module.structs:
|
||||||
|
for f in s.fields:
|
||||||
|
registerSlice(f.typ)
|
||||||
|
for e in module.enums:
|
||||||
|
for v in e.variants:
|
||||||
|
for ft in v.fields:
|
||||||
|
registerSlice(ft)
|
||||||
|
for nf in v.namedFields:
|
||||||
|
registerSlice(nf.typ)
|
||||||
|
|
||||||
|
# Build dependency graph among structs, enums, and slice types.
|
||||||
|
# Edge A -> B means "A depends on B, so B must be emitted before A".
|
||||||
|
var deps: Table[string, seq[string]]
|
||||||
|
for s in module.structs:
|
||||||
|
deps[s.name] = @[]
|
||||||
|
for e in module.enums:
|
||||||
|
deps[e.name] = @[]
|
||||||
|
for st in sliceTypes:
|
||||||
|
deps[st.name] = @[]
|
||||||
|
|
||||||
|
proc addDeps(node: string, t: Type) =
|
||||||
|
for dep in collectValueDeps(t):
|
||||||
|
if dep == node: continue
|
||||||
|
if localTypeNames.contains(dep) or sliceNames.contains(dep):
|
||||||
|
if dep notin deps[node]:
|
||||||
|
deps[node].add(dep)
|
||||||
|
|
||||||
|
for s in module.structs:
|
||||||
|
for f in s.fields:
|
||||||
|
addDeps(s.name, f.typ)
|
||||||
|
for e in module.enums:
|
||||||
|
for v in e.variants:
|
||||||
|
for ft in v.fields:
|
||||||
|
addDeps(e.name, ft)
|
||||||
|
for nf in v.namedFields:
|
||||||
|
addDeps(e.name, nf.typ)
|
||||||
|
# Multi-field / named-field nested struct must be defined before the enum
|
||||||
|
if v.fields.len > 1 or v.namedFields.len > 0:
|
||||||
|
addDeps(e.name, makeNamed(e.name & "_" & v.name & "_Payload"))
|
||||||
|
|
||||||
|
# Topological sort (Kahn's algorithm).
|
||||||
|
var inDegree: Table[string, int]
|
||||||
|
var dependents: Table[string, seq[string]]
|
||||||
|
for node in deps.keys:
|
||||||
|
inDegree[node] = 0
|
||||||
|
for node, nodeDeps in deps:
|
||||||
|
for d in nodeDeps:
|
||||||
|
if not inDegree.hasKey(d): inDegree[d] = 0
|
||||||
|
inDegree[node] += 1
|
||||||
|
dependents.mgetOrPut(d, @[]).add(node)
|
||||||
|
|
||||||
|
var queue: seq[string] = @[]
|
||||||
|
for node, deg in inDegree:
|
||||||
|
if deg == 0:
|
||||||
|
queue.add(node)
|
||||||
|
|
||||||
|
var sorted: seq[string] = @[]
|
||||||
|
while queue.len > 0:
|
||||||
|
let node = queue.pop()
|
||||||
|
sorted.add(node)
|
||||||
|
for depNode in dependents.getOrDefault(node):
|
||||||
|
inDegree[depNode] -= 1
|
||||||
|
if inDegree[depNode] == 0:
|
||||||
|
queue.add(depNode)
|
||||||
|
|
||||||
|
if sorted.len < deps.len:
|
||||||
|
# Cycle detected; fall back to a safe deterministic order.
|
||||||
|
sorted = @[]
|
||||||
|
for s in module.structs: sorted.add(s.name)
|
||||||
|
for e in module.enums: sorted.add(e.name)
|
||||||
|
for st in sliceTypes: sorted.add(st.name)
|
||||||
|
|
||||||
|
# Map type names back to their definitions.
|
||||||
|
var structMap: Table[string, seq[tuple[name: string, typ: Type]]]
|
||||||
|
for s in module.structs: structMap[s.name] = s.fields
|
||||||
|
var enumMap: Table[string, seq[HirEnumVariant]]
|
||||||
|
for e in module.enums: enumMap[e.name] = e.variants
|
||||||
|
var sliceMap: Table[string, string]
|
||||||
|
for st in sliceTypes: sliceMap[st.name] = st.elem
|
||||||
|
|
||||||
|
# Emit type definitions in dependency order.
|
||||||
|
for name in sorted:
|
||||||
|
if structMap.hasKey(name):
|
||||||
|
be.emitStructDef(name, structMap[name])
|
||||||
|
elif enumMap.hasKey(name):
|
||||||
|
be.emitEnumDef(name, enumMap[name])
|
||||||
|
elif sliceMap.hasKey(name):
|
||||||
|
be.emitSliceTypeDef(name, sliceMap[name])
|
||||||
|
|
||||||
# Fat function-pointer typedefs (BuxFn_*) — before forward decls that use them
|
# Fat function-pointer typedefs (BuxFn_*) — before forward decls that use them
|
||||||
var fatTypes: seq[Type] = @[]
|
var fatTypes: seq[Type] = @[]
|
||||||
var fatNames: HashSet[string]
|
var fatNames: HashSet[string]
|
||||||
|
|||||||
+30
-3
@@ -1330,12 +1330,18 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||||
# Look for the field in enum variants
|
# Look for the field in enum variants
|
||||||
for variant in enumSym.decl.declEnumVariants:
|
for variant in enumSym.decl.declEnumVariants:
|
||||||
# Check positional fields: Ok_0, Ok_1, etc.
|
# Multi-field / named-field variant: data.Variant → Enum_Variant_Payload
|
||||||
|
# (suffix avoids clashing with tag constant Enum_Variant)
|
||||||
|
if variant.fields.len > 1 and variant.name == expr.exprFieldName:
|
||||||
|
return makeNamed(enumName & "_" & variant.name & "_Payload")
|
||||||
|
if variant.namedFields.len > 0 and variant.name == expr.exprFieldName:
|
||||||
|
return makeNamed(enumName & "_" & variant.name & "_Payload")
|
||||||
|
# Single positional fields: Ok_0, Ok_1, etc. (flat on the union)
|
||||||
for i, f in variant.fields:
|
for i, f in variant.fields:
|
||||||
let fieldName = variant.name & "_" & $i
|
let fieldName = variant.name & "_" & $i
|
||||||
if fieldName == expr.exprFieldName:
|
if fieldName == expr.exprFieldName:
|
||||||
return sema.resolveType(f)
|
return sema.resolveType(f)
|
||||||
# Check named fields
|
# Named fields nested under data.Variant.name
|
||||||
for nf in variant.namedFields:
|
for nf in variant.namedFields:
|
||||||
if nf.name == expr.exprFieldName:
|
if nf.name == expr.exprFieldName:
|
||||||
return sema.resolveType(nf.ftype)
|
return sema.resolveType(nf.ftype)
|
||||||
@@ -1378,7 +1384,28 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
else:
|
else:
|
||||||
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
||||||
else:
|
else:
|
||||||
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
|
# Synthetic nested multi-field type Enum_Variant_Payload — generated for
|
||||||
|
# multi-field / named-field algebraic variants (not a user-declared type).
|
||||||
|
var foundNested = false
|
||||||
|
for (_, gsym) in sema.globalScope.table.pairs:
|
||||||
|
if gsym.decl == nil or gsym.decl.kind != dkEnum: continue
|
||||||
|
let ename = gsym.decl.declEnumName
|
||||||
|
for variant in gsym.decl.declEnumVariants:
|
||||||
|
let nestedName = ename & "_" & variant.name & "_Payload"
|
||||||
|
if nestedName != objType.name: continue
|
||||||
|
foundNested = true
|
||||||
|
for i, f in variant.fields:
|
||||||
|
let fieldName = variant.name & "_" & $i
|
||||||
|
if fieldName == expr.exprFieldName:
|
||||||
|
return sema.resolveType(f)
|
||||||
|
for nf in variant.namedFields:
|
||||||
|
if nf.name == expr.exprFieldName:
|
||||||
|
return sema.resolveType(nf.ftype)
|
||||||
|
sema.emitError(expr.loc, &"nested variant type '{objType.name}' has no field '{expr.exprFieldName}'")
|
||||||
|
return makeUnknown()
|
||||||
|
if not foundNested:
|
||||||
|
sema.emitError(expr.loc, &"undeclared type '{objType.name}'")
|
||||||
|
return makeUnknown()
|
||||||
elif objType.kind == tkDynRef:
|
elif objType.kind == tkDynRef:
|
||||||
# Trait object: methods come from the interface
|
# Trait object: methods come from the interface
|
||||||
let ifaceName = objType.name
|
let ifaceName = objType.name
|
||||||
|
|||||||
@@ -345,10 +345,21 @@ enum Result {
|
|||||||
Err(String)
|
Err(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Pair {
|
||||||
|
Two(int, int), // multi-field → nested payload
|
||||||
|
One(int), // single-field → flat data.One_0
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
func Main() -> int {
|
func Main() -> int {
|
||||||
let r: Result = Result { tag: Result_Ok };
|
let r: Result = Result { tag: Result_Ok };
|
||||||
r.data.Ok_0 = 42;
|
r.data.Ok_0 = 42;
|
||||||
|
|
||||||
|
// Multi-field construction: data.Variant.Variant_i
|
||||||
|
var p: Pair = Pair { tag: Pair_Two };
|
||||||
|
p.data.Two.Two_0 = 3;
|
||||||
|
p.data.Two.Two_1 = 4;
|
||||||
|
|
||||||
if r.tag == Result_Ok {
|
if r.tag == Result_Ok {
|
||||||
PrintInt(r.data.Ok_0);
|
PrintInt(r.data.Ok_0);
|
||||||
}
|
}
|
||||||
@@ -356,6 +367,10 @@ func Main() -> int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Layout notes:
|
||||||
|
- Single positional field: flat union member `data.Variant_0`
|
||||||
|
- Multi-field: nested payload `data.Variant.Variant_0` / `data.Variant.Variant_1` (C type `Enum_Variant_Payload`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pattern Matching
|
## Pattern Matching
|
||||||
@@ -383,6 +398,7 @@ Supported patterns:
|
|||||||
- Identifier catch-all: `name` (binds whole subject)
|
- Identifier catch-all: `name` (binds whole subject)
|
||||||
- Range: `1..9`, `1..=9`
|
- Range: `1..9`, `1..=9`
|
||||||
- Enum tags + **payload bindings**: `Option::Some(value)`, `Pair::Two(a, b)`
|
- Enum tags + **payload bindings**: `Option::Some(value)`, `Pair::Two(a, b)`
|
||||||
|
- **Nested**: `Box::Val((a, b))`, `Shape::Dot(Point { x, y })`
|
||||||
- **Tuple patterns**: `(a, b)` → binds `subject._0`, `subject._1`
|
- **Tuple patterns**: `(a, b)` → binds `subject._0`, `subject._1`
|
||||||
- **Struct patterns**: `Point { x: px, y: py }` or shorthand `Point { x, y }`
|
- **Struct patterns**: `Point { x: px, y: py }` or shorthand `Point { x, y }`
|
||||||
- Guard patterns: parsed; full lowering still evolving
|
- Guard patterns: parsed; full lowering still evolving
|
||||||
@@ -396,6 +412,14 @@ match p {
|
|||||||
Point { x, y } => x * 10 + y,
|
Point { x, y } => x * 10 + y,
|
||||||
_ => -1
|
_ => -1
|
||||||
}
|
}
|
||||||
|
match bx {
|
||||||
|
Box::Val((a, c)) => a + c,
|
||||||
|
Box::Empty => 0
|
||||||
|
}
|
||||||
|
match sh {
|
||||||
|
Shape::Dot(Point { x, y }) => x * 10 + y,
|
||||||
|
Shape::Empty => -1
|
||||||
|
}
|
||||||
|
|
||||||
// Multi-statement arm bodies (block expression; last expr is the value)
|
// Multi-statement arm bodies (block expression; last expr is the value)
|
||||||
match n {
|
match n {
|
||||||
|
|||||||
+16
-4
@@ -285,9 +285,21 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Сесия 17 (deeper nested patterns + multi-field enum layout)
|
||||||
|
|
||||||
|
1. **Multi-field payload type:** nested struct `Enum_Variant_Payload` (suffix avoids clash with tag `Enum_Variant`)
|
||||||
|
2. Bootstrap: full `resolveTypeExpr` for enum field types (tuples/pointers); sema synthetic field lookup on payload types; `data.Two.Two_0` construction
|
||||||
|
3. LIR C backend: emit tuple typedefs **before** enums that embed them; topo deps for `*_Payload`
|
||||||
|
4. Selfhost: enum variants parse full types (`parserParseType`) — fixes `Val((int,int))`; store `fieldTypeName*`; structs before enums (e.g. `Dot(Point)`)
|
||||||
|
5. Patterns: `Pair::Two(a, b)`, `Box::Val((a, c))`, `Shape::Dot(Point { x, y })` + block arms
|
||||||
|
6. Example: `examples/nested_patterns.bux`
|
||||||
|
7. Verified: bootstrap + **buxc2** + selfhost-loop IDENTICAL ✓
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Следващи стъпки
|
## Следващи стъпки
|
||||||
|
|
||||||
1. Deeper nested patterns (`Some((a, b))` with multi-field enum layout ergonomics)
|
1. LSP: wire hover types from real sema
|
||||||
2. LSP: wire hover types from real sema
|
2. Generic type inference for `Iter_Map` without explicit `<T,U>`
|
||||||
3. Generic type inference for `Iter_Map` without explicit `<T,U>`
|
3. Match arm guards (`p if cond => …`)
|
||||||
4. Match arm guards (`p if cond => …`)
|
4. Pattern binding name shadowing (C locals are function-scoped)
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Test::{Test_AssertEqInt, Test_Pass};
|
||||||
|
|
||||||
|
enum Pair {
|
||||||
|
Two(int, int),
|
||||||
|
One(int),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Box {
|
||||||
|
Val((int, int)),
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Shape {
|
||||||
|
Dot(Point),
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
func SumPair(p: Pair) -> int {
|
||||||
|
match p {
|
||||||
|
Pair::Two(a, b) => a + b,
|
||||||
|
Pair::One(x) => x,
|
||||||
|
Pair::None => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SumBox(bx: Box) -> int {
|
||||||
|
match bx {
|
||||||
|
Box::Val((a, c)) => a + c,
|
||||||
|
Box::Empty => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SumShape(sh: Shape) -> int {
|
||||||
|
match sh {
|
||||||
|
Shape::Dot(Point { x, y }) => x * 10 + y,
|
||||||
|
Shape::Empty => -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
var p: Pair = Pair { tag: Pair_Two };
|
||||||
|
p.data.Two.Two_0 = 3;
|
||||||
|
p.data.Two.Two_1 = 4;
|
||||||
|
Test_AssertEqInt(SumPair(p), 7);
|
||||||
|
|
||||||
|
var p1: Pair = Pair { tag: Pair_One };
|
||||||
|
p1.data.One_0 = 42;
|
||||||
|
Test_AssertEqInt(SumPair(p1), 42);
|
||||||
|
|
||||||
|
var bx: Box = Box { tag: Box_Val };
|
||||||
|
bx.data.Val_0 = (10, 20);
|
||||||
|
Test_AssertEqInt(SumBox(bx), 30);
|
||||||
|
|
||||||
|
let pt: Point = Point { x: 2, y: 5 };
|
||||||
|
var sh: Shape = Shape { tag: Shape_Dot };
|
||||||
|
sh.data.Dot_0 = pt;
|
||||||
|
Test_AssertEqInt(SumShape(sh), 25);
|
||||||
|
|
||||||
|
let z: int = match bx {
|
||||||
|
Box::Val((a, c)) => {
|
||||||
|
let s: int = a + c;
|
||||||
|
s * 2
|
||||||
|
},
|
||||||
|
Box::Empty => 0
|
||||||
|
};
|
||||||
|
Test_AssertEqInt(z, 60);
|
||||||
|
|
||||||
|
PrintInt(SumPair(p));
|
||||||
|
PrintLine("");
|
||||||
|
Test_Pass("nested_patterns");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+45
-39
@@ -1309,6 +1309,42 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
|
// Tuple typedefs before enums/structs that embed them by value
|
||||||
|
StringBuilder_Append(&cbe.sb, "/* Tuple types */\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int {\n int _0;\n int _1;\n} Tuple_int_int;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int_int {\n int _0;\n int _1;\n int _2;\n} Tuple_int_int_int;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_Empty {\n char _pad;\n} Tuple_Empty;\n\n");
|
||||||
|
|
||||||
|
// Struct definitions before enums (enums may embed structs by value, e.g. Shape::Dot(Point))
|
||||||
|
// Pass 1: emit structs with no value-typed struct fields (leaf structs)
|
||||||
|
si = 0;
|
||||||
|
while si < mod.structCount {
|
||||||
|
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||||
|
si = si + 1;
|
||||||
|
}
|
||||||
|
// Pass 2: emit structs that contain value-typed struct fields
|
||||||
|
si = 0;
|
||||||
|
while si < mod.structCount {
|
||||||
|
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||||
|
si = si + 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Enum definitions
|
// Enum definitions
|
||||||
var ei: int = 0;
|
var ei: int = 0;
|
||||||
while ei < mod.enumCount {
|
while ei < mod.enumCount {
|
||||||
@@ -1364,22 +1400,28 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
while vi < en.variantCount {
|
while vi < en.variantCount {
|
||||||
let ev: *HirEnumVariant = &en.variants[vi];
|
let ev: *HirEnumVariant = &en.variants[vi];
|
||||||
if ev.fieldCount > 0 {
|
if ev.fieldCount > 0 {
|
||||||
|
// Prefer stored C type name (handles Tuple_*, named types)
|
||||||
|
var ft0: String = CBackend_TypeToC(ev.fieldType0);
|
||||||
|
if !String_Eq(ev.fieldTypeName0, "") { ft0 = ev.fieldTypeName0; }
|
||||||
|
var ft1: String = CBackend_TypeToC(ev.fieldType1);
|
||||||
|
if !String_Eq(ev.fieldTypeName1, "") { ft1 = ev.fieldTypeName1; }
|
||||||
if ev.fieldCount == 1 {
|
if ev.fieldCount == 1 {
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
|
StringBuilder_Append(&cbe.sb, ft0);
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, ev.name);
|
StringBuilder_Append(&cbe.sb, ev.name);
|
||||||
StringBuilder_Append(&cbe.sb, "_0;\n");
|
StringBuilder_Append(&cbe.sb, "_0;\n");
|
||||||
} else {
|
} else {
|
||||||
|
// Nested anonymous struct (layout matches bootstrap data.Variant.Variant_i)
|
||||||
StringBuilder_Append(&cbe.sb, " struct {\n");
|
StringBuilder_Append(&cbe.sb, " struct {\n");
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
|
StringBuilder_Append(&cbe.sb, ft0);
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, ev.fieldName0);
|
StringBuilder_Append(&cbe.sb, ev.fieldName0);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
if ev.fieldCount > 1 {
|
if ev.fieldCount > 1 {
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType1));
|
StringBuilder_Append(&cbe.sb, ft1);
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, ev.fieldName1);
|
StringBuilder_Append(&cbe.sb, ev.fieldName1);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
@@ -1423,45 +1465,9 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct definitions (skip generics)
|
|
||||||
// Pass 1: emit structs with no value-typed struct fields (leaf structs)
|
|
||||||
si = 0;
|
|
||||||
while si < mod.structCount {
|
|
||||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
|
||||||
si = si + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if CBE_StructHasValueStructField(&mod.structs[si]) {
|
|
||||||
si = si + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
|
||||||
si = si + 1;
|
|
||||||
}
|
|
||||||
// Pass 2: emit structs that contain value-typed struct fields
|
|
||||||
si = 0;
|
|
||||||
while si < mod.structCount {
|
|
||||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
|
||||||
si = si + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !CBE_StructHasValueStructField(&mod.structs[si]) {
|
|
||||||
si = si + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
|
||||||
si = si + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fat function-pointer typedefs (BuxFn_*) — before forward decls
|
// Fat function-pointer typedefs (BuxFn_*) — before forward decls
|
||||||
CBE_EmitFatFuncTypedefs(cbe, mod);
|
CBE_EmitFatFuncTypedefs(cbe, mod);
|
||||||
|
|
||||||
// Common tuple struct types
|
|
||||||
StringBuilder_Append(&cbe.sb, "/* Tuple types */\n");
|
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int {\n int _0;\n int _1;\n} Tuple_int_int;\n");
|
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int_int {\n int _0;\n int _1;\n int _2;\n} Tuple_int_int_int;\n");
|
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_Empty {\n char _pad;\n} Tuple_Empty;\n\n");
|
|
||||||
|
|
||||||
// Env structs for capturing closures (no static instance — heap per value)
|
// Env structs for capturing closures (no static instance — heap per value)
|
||||||
var ei2: int = 0;
|
var ei2: int = 0;
|
||||||
while ei2 < mod.funcCount {
|
while ei2 < mod.funcCount {
|
||||||
|
|||||||
@@ -126,8 +126,10 @@ struct HirEnumVariant {
|
|||||||
fieldCount: int;
|
fieldCount: int;
|
||||||
fieldType0: int;
|
fieldType0: int;
|
||||||
fieldName0: String;
|
fieldName0: String;
|
||||||
|
fieldTypeName0: String; // C type name (e.g. "int", "Tuple_int_int")
|
||||||
fieldType1: int;
|
fieldType1: int;
|
||||||
fieldName1: String;
|
fieldName1: String;
|
||||||
|
fieldTypeName1: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+7
-2
@@ -782,9 +782,12 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
|
|||||||
dataLoad.child1 = dataPtr;
|
dataLoad.child1 = dataPtr;
|
||||||
dataLoad.typeName = String_Concat(enumName, "_Data");
|
dataLoad.typeName = String_Concat(enumName, "_Data");
|
||||||
|
|
||||||
// Multi-field: nested struct data.Variant; single-field: flat data.Variant_0
|
// Multi-field: nested struct data.Variant (anonymous or Enum_Variant_Payload);
|
||||||
|
// single-field: flat data.Variant_0
|
||||||
var payloadBase: *HirNode = dataLoad;
|
var payloadBase: *HirNode = dataLoad;
|
||||||
if fieldCount > 1 {
|
if fieldCount > 1 {
|
||||||
|
let nestedName: String = String_Concat(String_Concat(enumName, "_"),
|
||||||
|
String_Concat(variantName, "_Payload"));
|
||||||
let vPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
let vPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
vPtr.kind = hFieldPtr;
|
vPtr.kind = hFieldPtr;
|
||||||
vPtr.line = line;
|
vPtr.line = line;
|
||||||
@@ -796,7 +799,7 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
|
|||||||
vLoad.line = line;
|
vLoad.line = line;
|
||||||
vLoad.column = col;
|
vLoad.column = col;
|
||||||
vLoad.child1 = vPtr;
|
vLoad.child1 = vPtr;
|
||||||
vLoad.typeName = variantName;
|
vLoad.typeName = nestedName;
|
||||||
payloadBase = vLoad;
|
payloadBase = vLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3977,10 +3980,12 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
// Positional field names: Variant_0, Variant_1 (matches data.Variant_i / nested struct)
|
// Positional field names: Variant_0, Variant_1 (matches data.Variant_i / nested struct)
|
||||||
hm.enums[ei].variants[vi].fieldName0 = String_Concat(v.name, "_0");
|
hm.enums[ei].variants[vi].fieldName0 = String_Concat(v.name, "_0");
|
||||||
hm.enums[ei].variants[vi].fieldType0 = Lcx_ResolveTypeKindFromName(v.fieldTypeName0);
|
hm.enums[ei].variants[vi].fieldType0 = Lcx_ResolveTypeKindFromName(v.fieldTypeName0);
|
||||||
|
hm.enums[ei].variants[vi].fieldTypeName0 = v.fieldTypeName0;
|
||||||
}
|
}
|
||||||
if v.fieldCount > 1 {
|
if v.fieldCount > 1 {
|
||||||
hm.enums[ei].variants[vi].fieldName1 = String_Concat(v.name, "_1");
|
hm.enums[ei].variants[vi].fieldName1 = String_Concat(v.name, "_1");
|
||||||
hm.enums[ei].variants[vi].fieldType1 = Lcx_ResolveTypeKindFromName(v.fieldTypeName1);
|
hm.enums[ei].variants[vi].fieldType1 = Lcx_ResolveTypeKindFromName(v.fieldTypeName1);
|
||||||
|
hm.enums[ei].variants[vi].fieldTypeName1 = v.fieldTypeName1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vi = vi + 1;
|
vi = vi + 1;
|
||||||
|
|||||||
+27
-5
@@ -148,6 +148,28 @@ func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken {
|
|||||||
// Type parsing
|
// Type parsing
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// C-friendly type name from a TypeExpr (named, tuple, pointer, …).
|
||||||
|
func parserTypeExprCName(te: *TypeExpr) -> String {
|
||||||
|
if te == null as *TypeExpr { return "int"; }
|
||||||
|
if te.kind == tekTuple {
|
||||||
|
if !String_Eq(te.typeName, "") { return te.typeName; }
|
||||||
|
return "Tuple_Empty";
|
||||||
|
}
|
||||||
|
if te.kind == tekPointer || te.kind == tekRef || te.kind == tekMutRef {
|
||||||
|
if te.pointerPointee != null as *TypeExpr {
|
||||||
|
return String_Concat(parserTypeExprCName(te.pointerPointee), "*");
|
||||||
|
}
|
||||||
|
return "void*";
|
||||||
|
}
|
||||||
|
if !String_Eq(te.typeName, "") {
|
||||||
|
if String_Eq(te.typeName, "String") || String_Eq(te.typeName, "str") {
|
||||||
|
return "const char*";
|
||||||
|
}
|
||||||
|
return te.typeName;
|
||||||
|
}
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
func parserParseType(p: *Parser) -> *TypeExpr {
|
func parserParseType(p: *Parser) -> *TypeExpr {
|
||||||
let line: uint32 = parserCurToken(p).line;
|
let line: uint32 = parserCurToken(p).line;
|
||||||
let col: uint32 = parserCurToken(p).column;
|
let col: uint32 = parserCurToken(p).column;
|
||||||
@@ -1953,14 +1975,14 @@ func parserParseEnumDecl(p: *Parser, isPublic: bool) -> *Decl {
|
|||||||
v.fieldTypeName0 = "";
|
v.fieldTypeName0 = "";
|
||||||
v.fieldTypeName1 = "";
|
v.fieldTypeName1 = "";
|
||||||
|
|
||||||
// Optional (Type, Type) data
|
// Optional payload: (Type, Type) or (Tuple, ...) — full type expressions
|
||||||
if parserMatch(p, tkLParen) {
|
if parserMatch(p, tkLParen) {
|
||||||
let t0: LexToken = parserExpect(p, tkIdent, "expected data type");
|
let te0: *TypeExpr = parserParseType(p);
|
||||||
v.fieldTypeName0 = t0.text;
|
v.fieldTypeName0 = parserTypeExprCName(te0);
|
||||||
v.fieldCount = 1;
|
v.fieldCount = 1;
|
||||||
if parserMatch(p, tkComma) {
|
if parserMatch(p, tkComma) {
|
||||||
let t1: LexToken = parserExpect(p, tkIdent, "expected data type");
|
let te1: *TypeExpr = parserParseType(p);
|
||||||
v.fieldTypeName1 = t1.text;
|
v.fieldTypeName1 = parserTypeExprCName(te1);
|
||||||
v.fieldCount = 2;
|
v.fieldCount = 2;
|
||||||
}
|
}
|
||||||
discard parserExpect(p, tkRParen, "expected ')'");
|
discard parserExpect(p, tkRParen, "expected ')'");
|
||||||
|
|||||||
Reference in New Issue
Block a user