feat(selfhost): full tuple type support with .0/.1 field access

Parse (T, U) types and (a, b) expressions, lower to Tuple_* C structs,
and emit common tuple typedefs so buxc2 matches bootstrap tuple codegen.
This commit is contained in:
2026-07-15 16:04:01 +03:00
parent 61ac06ab5f
commit 9efba57b4c
6 changed files with 191 additions and 8 deletions
+95 -4
View File
@@ -231,6 +231,57 @@ func parserParseType(p: *Parser) -> *TypeExpr {
return te;
}
// (T, U, ...) tuple type
if kindTok == tkLParen {
discard parserAdvance(p);
var elems: *TypeExprList = null as *TypeExprList;
var elemsTail: *TypeExprList = null as *TypeExprList;
var count: int = 0;
var typeName: String = "Tuple";
while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile {
let elemTe: *TypeExpr = parserParseType(p);
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
node.te = elemTe;
node.next = null as *TypeExprList;
if elems == null as *TypeExprList {
elems = node;
} else {
elemsTail.next = node;
}
elemsTail = node;
count = count + 1;
// Build mangled name: Tuple_int_int
var part: String = "int";
if elemTe != null as *TypeExpr {
if !String_Eq(elemTe.typeName, "") {
part = elemTe.typeName;
} else if elemTe.kind == tekPointer && elemTe.pointerPointee != null as *TypeExpr {
part = String_Concat(elemTe.pointerPointee.typeName, "Ptr");
}
}
if String_Eq(part, "String") || String_Eq(part, "str") { part = "cstr"; }
typeName = String_Concat(typeName, "_");
typeName = String_Concat(typeName, part);
if parserCheck(p, tkComma) {
discard parserAdvance(p);
} else {
break;
}
}
discard parserExpect(p, tkRParen, "expected ')' to close tuple type");
if count == 0 {
typeName = "Tuple_Empty";
}
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
te.kind = tekTuple;
te.line = line;
te.column = col;
te.tupleElems = elems;
te.tupleCount = count;
te.typeName = typeName;
return te;
}
// name
let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name");
// self / Self -> tekSelf
@@ -386,12 +437,43 @@ func parserParsePrimary(p: *Parser) -> *Expr {
return e;
}
// ( expr )
// ( expr ) or (a, b, ...) tuple
if kind == tkLParen {
discard parserAdvance(p);
let e: *Expr = parserParseExpr(p);
// Empty tuple ()
if parserCheck(p, tkRParen) {
discard parserAdvance(p);
let te: *Expr = parserMakeExpr(ekTuple, line, col);
te.callArgCount = 0;
return te;
}
let first: *Expr = parserParseExpr(p);
if parserCheck(p, tkComma) {
// Tuple expression
let te: *Expr = parserMakeExpr(ekTuple, line, col);
var firstArg: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
firstArg.expr = first;
firstArg.next = null as *ExprList;
var lastArg: *ExprList = firstArg;
var count: int = 1;
while parserCheck(p, tkComma) {
discard parserAdvance(p);
if parserCheck(p, tkRParen) { break; }
let elem: *Expr = parserParseExpr(p);
let node: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
node.expr = elem;
node.next = null as *ExprList;
lastArg.next = node;
lastArg = node;
count = count + 1;
}
discard parserExpect(p, tkRParen, "expected ')' to close tuple");
te.callArgs = firstArg;
te.callArgCount = count;
return te;
}
discard parserExpect(p, tkRParen, "expected ')'");
return e;
return first;
}
// Closure: |params| -> Ret { body }
@@ -586,11 +668,20 @@ func parserParsePostfixExpr(p: *Parser) -> *Expr {
}
}
// Field: expr.name
// Field: expr.name or tuple index expr.0 / expr.1
if kind == tkDot {
discard parserAdvance(p);
let line: uint32 = parserCurToken(p).line;
let col: uint32 = parserCurToken(p).column;
if parserCheck(p, tkIntLiteral) {
let idxTok: LexToken = parserCurToken(p);
discard parserAdvance(p);
let e: *Expr = parserMakeExpr(ekField, line, col);
e.child1 = left;
e.strValue = String_Concat("_", idxTok.text);
left = e;
continue;
}
let name: LexToken = parserExpectIdentOrKeyword(p, "expected field name");
let e: *Expr = parserMakeExpr(ekField, line, col);
e.child1 = left;