// parser.bux — Recursive descent parser (ported from parser.nim)
// Parses Bux source tokens into an AST.
module Parser {

extern func bux_strlen(s: String) -> uint;
extern func bux_str_to_int(s: String) -> int64;

// Forward declarations for mutual recursion
func parserParseExpr(p: *Parser) -> *Expr;
func parserParseStmt(p: *Parser) -> *Stmt;
func parserParseBlock(p: *Parser) -> *Block;
func parserParsePrimary(p: *Parser) -> *Expr;
func parserParsePostfixExpr(p: *Parser) -> *Expr;
func parserParseUnary(p: *Parser) -> *Expr;
func parserParseBinaryPrec(p: *Parser, minPrec: int) -> *Expr;

// ---------------------------------------------------------------------------
// Parser state
// ---------------------------------------------------------------------------
struct Parser {
    tokens: *LexToken,
    tokenCount: int,
    pos: int,
    diagCount: int,
    diags: *ParserDiag,
    structInitAllowed: bool,
}

struct ParserDiag {
    line: uint32,
    column: uint32,
    message: String,
}

// ---------------------------------------------------------------------------
// Token helpers
// ---------------------------------------------------------------------------

func parserCurToken(p: *Parser) -> LexToken {
    if p.pos < p.tokenCount {
        return p.tokens[p.pos];
    }
    var eof: LexToken;
    eof.kind = tkEndOfFile;
    eof.text = "";
    return eof;
}

func parserPeek(p: *Parser, ahead: int) -> int {
    let i: int = p.pos + ahead;
    if i >= 0 && i < p.tokenCount {
        return p.tokens[i].kind;
    }
    return tkEndOfFile;
}

// Lookahead to determine if '<' starts a type argument list.
func parserIsTypeArgListAhead(p: *Parser) -> bool {
    if !parserCheck(p, tkLt) { return false; }
    var depth: int = 0;
    var ahead: int = 0;
    while true {
        let kind: int = parserPeek(p, ahead);
        if kind == tkEndOfFile || kind == tkLBrace || kind == tkSemicolon {
            return false;
        }
        if kind == tkLt {
            depth = depth + 1;
        } else if kind == tkGt {
            depth = depth - 1;
            if depth == 0 {
                return true;
            }
        }
        ahead = ahead + 1;
    }
    return false;
}

func parserAdvance(p: *Parser) -> LexToken {
    let tok: LexToken = parserCurToken(p);
    if p.pos < p.tokenCount {
        p.pos = p.pos + 1;
    }
    return tok;
}

func parserCheck(p: *Parser, kind: int) -> bool {
    return parserPeek(p, 0) == kind;
}

func parserMatch(p: *Parser, kind: int) -> bool {
    if parserCheck(p, kind) {
        discard parserAdvance(p);
        return true;
    }
    return false;
}

func parserPrevious(p: *Parser) -> LexToken {
    if p.pos > 0 && p.pos <= p.tokenCount {
        return p.tokens[p.pos - 1];
    }
    var eof: LexToken;
    eof.kind = tkEndOfFile;
    return eof;
}

func parserExpect(p: *Parser, kind: int, msg: String) -> LexToken {
    if parserCheck(p, kind) {
        return parserAdvance(p);
    }
    let tok: LexToken = parserCurToken(p);
    if p.diagCount < 256 {
        p.diags[p.diagCount] = ParserDiag {
            line: tok.line, column: tok.column, message: msg
        };
        p.diagCount = p.diagCount + 1;
    }
    return tok;
}

func parserEmitDiag(p: *Parser, line: uint32, col: uint32, msg: String) {
    if p.diagCount < 256 {
        p.diags[p.diagCount] = ParserDiag {
            line: line, column: col, message: msg
        };
        p.diagCount = p.diagCount + 1;
    }
}

func parserIsKeyword(kind: int) -> bool {
    if kind >= tkIf && kind <= tkSuper { return true; }
    if kind == tkSizeOf { return true; }
    return false;
}

func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken {
    let tok: LexToken = parserCurToken(p);
    if tok.kind == tkIdent || parserIsKeyword(tok.kind) {
        return parserAdvance(p);
    }
    if p.diagCount < 256 {
        p.diags[p.diagCount] = ParserDiag {
            line: tok.line, column: tok.column, message: msg
        };
        p.diagCount = p.diagCount + 1;
    }
    return tok;
}

// ---------------------------------------------------------------------------
// Type parsing
// ---------------------------------------------------------------------------

func parserParseType(p: *Parser) -> *TypeExpr {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    let kindTok: int = parserPeek(p, 0);

    // *T (pointer)
    if kindTok == tkStar {
        discard parserAdvance(p);
        let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
        te.kind = tekPointer;
        te.line = line;
        te.column = col;
        te.pointerPointee = parserParseType(p);
        // Set typeName to "Pointee*"
        if te.pointerPointee != null as *TypeExpr {
            te.typeName = String_Concat(te.pointerPointee.typeName, "*");
        }
        return te;
    }

    // name
    let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name");
    let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
    te.kind = tekNamed;
    te.line = nameTok.line;
    te.column = nameTok.column;
    te.typeName = nameTok.text;

    // Optional <T, U> type args
    if parserCheck(p, tkLt) {
        discard parserAdvance(p); // <
        let arg0: LexToken = parserExpect(p, tkIdent, "expected type argument");
        te.typeArgName0 = arg0.text;
        te.typeArgCount = 1;
        if parserMatch(p, tkComma) {
            let arg1: LexToken = parserExpect(p, tkIdent, "expected type argument");
            te.typeArgName1 = arg1.text;
            te.typeArgCount = 2;
        }
        discard parserExpect(p, tkGt, "expected '>' to close type arguments");
    }
    return te;
}

// ---------------------------------------------------------------------------
// Forward declarations and helpers
// ---------------------------------------------------------------------------

func parserParseExpr(p: *Parser) -> *Expr;
func parserParseStmt(p: *Parser) -> *Stmt;
func parserParseBlock(p: *Parser) -> *Block;

func parserMakeExpr(kind: int, line: uint32, col: uint32) -> *Expr {
    let e: *Expr = bux_alloc(sizeof(Expr)) as *Expr;
    e.kind = kind;
    e.line = line;
    e.column = col;
    e.strValue = "";
    e.intValue = 0;
    e.boolValue = false;
    e.tokKind = 0;
    e.tokText = "";
    e.child1 = null as *Expr;
    e.child2 = null as *Expr;
    e.child3 = null as *Expr;
    e.refType = null as *TypeExpr;
    e.refBlock = null as *Block;
    e.genericCallee = "";
    e.genericTypeArg0 = "";
    e.genericTypeArg1 = "";
    e.genericTypeArgCount = 0;
    e.structName = "";
    e.structFieldCount = 0;
    e.callArgs = null as *ExprList;
    e.callArgCount = 0;
    return e;
}

// ---------------------------------------------------------------------------
// Primary expressions
// ---------------------------------------------------------------------------

func parserParsePrimary(p: *Parser) -> *Expr {
    let tok: LexToken = parserCurToken(p);
    let line: uint32 = tok.line;
    let col: uint32 = tok.column;
    let kind: int = tok.kind;

    // Literals
    if kind == tkIntLiteral || kind == tkFloatLiteral || kind == tkStringLiteral
        || kind == tkCharLiteral || kind == tkBoolLiteral {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekLiteral, line, col);
        e.tokKind = kind;
        e.tokText = tok.text;
        if kind == tkIntLiteral {
            e.intValue = bux_str_to_int(tok.text) as int;
        }
        return e;
    }

    // Identifier
    if kind == tkIdent {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekIdent, line, col);
        e.strValue = tok.text;
        return e;
    }

    // self
    if kind == tkSelf {
        discard parserAdvance(p);
        return parserMakeExpr(ekSelf, line, col);
    }

    // null
    if kind == tkNull {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekLiteral, line, col);
        e.tokKind = tkNull;
        return e;
    }

    // sizeof(Type)
    if kind == tkSizeOf {
        discard parserAdvance(p);
        discard parserExpect(p, tkLParen, "expected '(' after sizeof");
        let e: *Expr = parserMakeExpr(ekSizeOf, line, col);
        e.refType = parserParseType(p);
        discard parserExpect(p, tkRParen, "expected ')' after sizeof type");
        return e;
    }

    // spawn Callee(args)
    if kind == tkSpawn {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekSpawn, line, col);
        e.child1 = parserParsePrimary(p);
        // Optional call arguments
        if parserCheck(p, tkLParen) {
            discard parserAdvance(p);
            if !parserCheck(p, tkRParen) {
                e.child2 = parserParseExpr(p);
                if parserMatch(p, tkComma) {
                    e.child3 = parserParseExpr(p);
                    while parserMatch(p, tkComma) {
                        discard parserParseExpr(p);
                    }
                }
            }
            discard parserExpect(p, tkRParen, "expected ')' after spawn arguments");
        }
        return e;
    }

    // #intrinsics
    if kind >= tkHashLine && kind <= tkHashModule {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekLiteral, line, col);
        e.intValue = kind;
        return e;
    }

    // ( expr )
    if kind == tkLParen {
        discard parserAdvance(p);
        let e: *Expr = parserParseExpr(p);
        discard parserExpect(p, tkRParen, "expected ')'");
        return e;
    }

    parserEmitDiag(p, line, col, "expected expression");
    return parserMakeExpr(ekLiteral, line, col);
}

// ---------------------------------------------------------------------------
// Postfix: call, index, field access, as, is, ?
// ---------------------------------------------------------------------------

func parserParsePostfixExpr(p: *Parser) -> *Expr {
    var left: *Expr = parserParsePrimary(p);

    while true {
        let kind: int = parserPeek(p, 0);

        // Call: expr(args)
        if kind == tkLParen {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let e: *Expr = parserMakeExpr(ekCall, line, col);
            e.child1 = left;
            // Parse all arguments into linked list
            var argCount: int = 0;
            var firstArg: *ExprList = null as *ExprList;
            var lastArg: *ExprList = null as *ExprList;
            while !parserCheck(p, tkRParen) {
                let argExpr: *Expr = parserParseExpr(p);
                let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
                argNode.expr = argExpr;
                argNode.next = null as *ExprList;
                if firstArg == null as *ExprList {
                    firstArg = argNode;
                    lastArg = argNode;
                } else {
                    lastArg.next = argNode;
                    lastArg = argNode;
                }
                argCount = argCount + 1;
                if !parserMatch(p, tkComma) { break; }
            }
            e.callArgs = firstArg;
            e.callArgCount = argCount;
            discard parserExpect(p, tkRParen, "expected ')'");
            left = e;
            continue;
        }

        // Generic call: Func<Type>(args)  or  Type<T> { ... }
        if kind == tkLt {
            if left.kind == ekIdent && parserIsTypeArgListAhead(p) {
                discard parserAdvance(p); // <
                let ta0: LexToken = parserExpect(p, tkIdent, "expected type argument");
                left.genericCallee = left.strValue;
                left.genericTypeArg0 = ta0.text;
                left.genericTypeArgCount = 1;
                if parserMatch(p, tkComma) {
                    let ta1: LexToken = parserExpect(p, tkIdent, "expected type argument");
                    left.genericTypeArg1 = ta1.text;
                    left.genericTypeArgCount = 2;
                }
                discard parserExpect(p, tkGt, "expected '>' to close type arguments");
                // After generic args, continue loop to handle call or struct init
                continue;
            }
        }

        // Index: expr[expr]
        if kind == tkLBracket {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let e: *Expr = parserMakeExpr(ekIndex, line, col);
            e.child1 = left;
            e.child2 = parserParseExpr(p);
            discard parserExpect(p, tkRBracket, "expected ']'");
            left = e;
            continue;
        }

        // .await
        if kind == tkDot {
            if parserPeek(p, 1) == tkAwait {
                discard parserAdvance(p); // .
                discard parserAdvance(p); // await
                let line: uint32 = parserCurToken(p).line;
                let col: uint32 = parserCurToken(p).column;
                let e: *Expr = parserMakeExpr(ekAwait, line, col);
                e.child1 = left;
                left = e;
                continue;
            }
        }

        // Field: expr.name
        if kind == tkDot {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let name: LexToken = parserExpect(p, tkIdent, "expected field name");
            let e: *Expr = parserMakeExpr(ekField, line, col);
            e.child1 = left;
            e.strValue = name.text;
            left = e;
            continue;
        }

        // Path: expr::name
        if kind == tkColonColon {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let name: LexToken = parserExpect(p, tkIdent, "expected path segment");
            let e: *Expr = parserMakeExpr(ekField, line, col);
            e.child1 = left;
            e.strValue = name.text;
            left = e;
            continue;
        }

        // as, is
        if kind == tkAs || kind == tkIs {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let ek: int = 0;
            if kind == tkAs { ek = ekCast; } else { ek = ekIs; }
            let e: *Expr = parserMakeExpr(ek, line, col);
            e.child1 = left;
            e.refType = parserParseType(p);
            left = e;
            continue;
        }

        // ? (try operator)
        if kind == tkQuestion {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let e: *Expr = parserMakeExpr(ekTry, line, col);
            e.child1 = left;
            left = e;
            continue;
        }

        // ! (unwrap operator)
        if kind == tkBang {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let e: *Expr = parserMakeExpr(ekUnwrap, line, col);
            e.child1 = left;
            left = e;
            continue;
        }

        // ++, --
        if kind == tkPlusPlus || kind == tkMinusMinus {
            discard parserAdvance(p);
            let line: uint32 = parserCurToken(p).line;
            let col: uint32 = parserCurToken(p).column;
            let e: *Expr = parserMakeExpr(ekPostfix, line, col);
            e.child1 = left;
            e.intValue = kind;
            left = e;
            continue;
        }

        // Struct init: TypeName { field: value, ... }
        if kind == tkLBrace {
            if p.structInitAllowed && left.kind == ekIdent {
                discard parserAdvance(p); // consume {
                let siLine: uint32 = parserCurToken(p).line;
                let siCol: uint32 = parserCurToken(p).column;
                let typeName: String = left.strValue;
                var fieldCount: int = 0;
                var firstField: *Expr = null as *Expr;
                var lastField: *Expr = null as *Expr;
                while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
                    while parserCheck(p, tkNewLine) { discard parserAdvance(p); }
                    if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; }
                    let sip: int = p.pos;
                    let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name");
                    discard parserExpect(p, tkColon, "expected ':'");
                    let fValue: *Expr = parserParseExpr(p);
                    let fExpr: *Expr = parserMakeExpr(ekField, fName.line, fName.column);
                    fExpr.strValue = fName.text;
                    fExpr.child1 = fValue;
                    if firstField == null as *Expr {
                        firstField = fExpr;
                        lastField = fExpr;
                    } else {
                        lastField.child3 = fExpr;
                        lastField = fExpr;
                    }
                    fieldCount = fieldCount + 1;
                    parserMatch(p, tkComma);
                    if p.pos == sip { discard parserAdvance(p); }
                }
                discard parserExpect(p, tkRBrace, "expected '}'");
                let e: *Expr = parserMakeExpr(ekStructInit, siLine, siCol);
                e.structName = typeName;
                e.structFieldCount = fieldCount;
                e.child1 = firstField;
                left = e;
                continue;
            } else {
                break;
            }
        }

        break;
    }
    return left;
}

// ---------------------------------------------------------------------------
// Binary expression (precedence climbing)
// All binary operators: arithmetic, comparison, logical, bitwise, assignment
// ---------------------------------------------------------------------------

func parserIsBinaryOp(kind: int) -> bool {
    if kind >= tkPlus && kind <= tkStarStar { return true; }           // arithmetic
    if kind == tkAmp || kind == tkPipe || kind == tkCaret { return true; } // bitwise (no &|)
    if kind >= tkShl && kind <= tkShrAssign { return true; }           // shift + assign
    if kind >= tkEq && kind <= tkGe { return true; }                   // comparison
    if kind == tkAmpAmp || kind == tkPipePipe { return true; }         // logical
    if kind >= tkAssign && kind <= tkShrAssign { return true; }        // all assigns
    if kind == tkDotDot || kind == tkDotDotEqual { return true; }      // range
    return false;
}

func parserPrecedence(op: int) -> int {
    if op == tkAssign || op == tkPlusAssign || op == tkMinusAssign || op == tkStarAssign || op == tkSlashAssign || op == tkPercentAssign || op == tkAmpAssign || op == tkPipeAssign || op == tkCaretAssign || op == tkShlAssign || op == tkShrAssign { return 1; }
    if op == tkPipePipe { return 2; }
    if op == tkAmpAmp { return 3; }
    if op == tkPipe { return 4; }
    if op == tkCaret { return 5; }
    if op == tkAmp { return 6; }
    if op == tkEq || op == tkNe { return 7; }
    if op == tkLt || op == tkLe || op == tkGt || op == tkGe { return 8; }
    if op == tkShl || op == tkShr { return 9; }
    if op == tkPlus || op == tkMinus { return 10; }
    if op == tkStar || op == tkSlash || op == tkPercent { return 11; }
    if op == tkStarStar { return 12; }
    return 0;
}

func parserParseUnary(p: *Parser) -> *Expr {
    let tok: LexToken = parserCurToken(p);
    let line: uint32 = tok.line;
    let col: uint32 = tok.column;
    let kind: int = tok.kind;

    // -expr, !expr, *expr, &expr
    if kind == tkMinus || kind == tkBang || kind == tkStar || kind == tkAmp {
        discard parserAdvance(p);
        let e: *Expr = parserMakeExpr(ekUnary, line, col);
        e.intValue = kind;
        e.child1 = parserParseUnary(p);
        return e;
    }

    return parserParsePostfixExpr(p);
}

func parserParseBinaryPrec(p: *Parser, minPrec: int) -> *Expr {
    var left: *Expr = parserParseUnary(p);
    while true {
        let op: int = parserPeek(p, 0);
        let prec: int = parserPrecedence(op);
        if prec < minPrec { break; }
        let opTok: LexToken = parserAdvance(p);
        let line: uint32 = opTok.line;
        let col: uint32 = opTok.column;
        let nextMinPrec: int = prec + 1;
        let right: *Expr = parserParseBinaryPrec(p, nextMinPrec);
        let e: *Expr = parserMakeExpr(ekBinary, line, col);
        e.intValue = opTok.kind;
        e.child1 = left;
        e.child2 = right;
        left = e;
    }
    return left;
}

func parserParseBinary(p: *Parser) -> *Expr {
    return parserParseBinaryPrec(p, 1);
}

// ---------------------------------------------------------------------------
// Ternary: cond ? then : else
// ---------------------------------------------------------------------------

func parserParseTernary(p: *Parser) -> *Expr {
    var left: *Expr = parserParseBinary(p);
    if parserMatch(p, tkQuestion) {
        let thenExpr: *Expr = parserParseExpr(p);
        discard parserExpect(p, tkColon, "expected ':' in ternary");
        let elseExpr: *Expr = parserParseExpr(p);
        let e: *Expr = parserMakeExpr(ekTernary, left.line, left.column);
        e.child1 = left;
        e.child2 = thenExpr;
        e.child3 = elseExpr;
        return e;
    }
    return left;
}

// ---------------------------------------------------------------------------
// Top-level expression
// ---------------------------------------------------------------------------

func parserParseExpr(p: *Parser) -> *Expr {
    return parserParseTernary(p);
}

// ---------------------------------------------------------------------------
// Statements
// ---------------------------------------------------------------------------

func parserParseStmt(p: *Parser) -> *Stmt {
    let tok: LexToken = parserCurToken(p);
    let line: uint32 = tok.line;
    let col: uint32 = tok.column;
    let kind: int = tok.kind;

    // let / var
    if kind == tkLet || kind == tkVar {
        let isVar: bool = (kind == tkVar);
        discard parserAdvance(p);
        let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected variable name");
        var typeExpr: *TypeExpr = null as *TypeExpr;
        if parserMatch(p, tkColon) {
            typeExpr = parserParseType(p);
        }
        var init: *Expr = null as *Expr;
        if parserMatch(p, tkAssign) {
            init = parserParseExpr(p);
        } else if !isVar {
            discard parserExpect(p, tkAssign, "expected '=' in let statement");
            init = parserParseExpr(p);
        }
        parserMatch(p, tkSemicolon); // optional ;

        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skLet;
        s.line = line;
        s.column = col;
        s.strValue = nameTok.text;
        s.boolValue = isVar;
        s.child1 = init;
        s.refStmtType = typeExpr;
        return s;
    }

    // discard expr
    if kind == tkDiscard {
        discard parserAdvance(p);
        var val: *Expr = null as *Expr;
        if !parserCheck(p, tkSemicolon) && !parserCheck(p, tkRBrace) && !parserCheck(p, tkNewLine) {
            val = parserParseExpr(p);
        }
        parserMatch(p, tkSemicolon);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skExpr;
        s.line = line;
        s.column = col;
        s.child1 = val;
        return s;
    }

    // if
    if kind == tkIf {
        discard parserAdvance(p);
        p.structInitAllowed = false;
        let cond: *Expr = parserParseExpr(p);
        p.structInitAllowed = true;
        let thenBlock: *Block = parserParseBlock(p);
        var elseBlock: *Block = null as *Block;
        while parserCheck(p, tkNewLine) {
            discard parserAdvance(p);
        }
        if parserMatch(p, tkElse) {
            if parserCheck(p, tkIf) {
                // else if → parse the if statement, wrap in a synthetic block
                let innerIf: *Stmt = parserParseStmt(p);
                elseBlock = bux_alloc(sizeof(Block)) as *Block;
                elseBlock.line = innerIf.line;
                elseBlock.column = innerIf.column;
                elseBlock.stmtCount = 1;
                elseBlock.firstStmt = innerIf;
                elseBlock.lastStmt = innerIf;
                innerIf.nextStmt = null as *Stmt;
            } else {
                elseBlock = parserParseBlock(p);
            }
        }
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skIf;
        s.line = line;
        s.column = col;
        s.child1 = cond;
        s.refStmtBlock = thenBlock;
        s.refStmtElse = elseBlock;
        return s;
    }

    // while
    if kind == tkWhile {
        discard parserAdvance(p);
        p.structInitAllowed = false;
        let cond: *Expr = parserParseExpr(p);
        p.structInitAllowed = true;
        let body: *Block = parserParseBlock(p);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skWhile;
        s.line = line;
        s.column = col;
        s.child1 = cond;
        s.refStmtBlock = body;
        return s;
    }

    // loop
    if kind == tkLoop {
        discard parserAdvance(p);
        let body: *Block = parserParseBlock(p);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skLoop;
        s.line = line;
        s.column = col;
        s.refStmtBlock = body;
        return s;
    }

    // for
    if kind == tkFor {
        discard parserAdvance(p);
        let varName: LexToken = parserExpect(p, tkIdent, "expected loop variable");
        discard parserExpect(p, tkIn, "expected 'in'");
        p.structInitAllowed = false;
        let iter: *Expr = parserParseExpr(p);
        p.structInitAllowed = true;
        let body: *Block = parserParseBlock(p);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skFor;
        s.line = line;
        s.column = col;
        s.strValue = varName.text;
        s.child1 = iter;
        s.refStmtBlock = body;
        return s;
    }

    // return
    if kind == tkReturn {
        discard parserAdvance(p);
        var value: *Expr = null as *Expr;
        if !parserCheck(p, tkSemicolon) && !parserCheck(p, tkNewLine) && !parserCheck(p, tkRBrace) {
            value = parserParseExpr(p);
        }
        parserMatch(p, tkSemicolon);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skReturn;
        s.line = line;
        s.column = col;
        s.child1 = value;
        return s;
    }

    // break / continue
    if kind == tkBreak || kind == tkContinue {
        let sk: int = 0;
        if kind == tkBreak { sk = skBreak; } else { sk = skContinue; }
        discard parserAdvance(p);
        parserMatch(p, tkSemicolon);
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = sk;
        s.line = line;
        s.column = col;
        return s;
    }

    // match
    if kind == tkMatch {
        discard parserAdvance(p);
        p.structInitAllowed = false;
        let subject: *Expr = parserParseExpr(p);
        p.structInitAllowed = true;
        discard parserExpect(p, tkLBrace, "expected '{' to start match body");
        // Skip match body (simplified — just parse arms as empty)
        while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
            if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
            let mp: int = p.pos;
            discard parserParseExpr(p);  // pattern
            if parserMatch(p, tkFatArrow) {
                discard parserParseExpr(p);  // body
            }
            if p.pos == mp { discard parserAdvance(p); }
        }
        discard parserExpect(p, tkRBrace, "expected '}' to close match");
        let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
        s.kind = skMatch;
        s.line = line;
        s.column = col;
        s.child1 = subject;
        return s;
    }

    // Expression statement
    if kind == tkNewLine || kind == tkSemicolon {
        discard parserAdvance(p);
        return null as *Stmt;
    }

    let expr: *Expr = parserParseExpr(p);
    parserMatch(p, tkSemicolon);
    let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
    s.kind = skExpr;
    s.line = line;
    s.column = col;
    s.child1 = expr;
    return s;
}

// ---------------------------------------------------------------------------
// Block: { stmt* }
// ---------------------------------------------------------------------------

func parserParseBlock(p: *Parser) -> *Block {
    discard parserExpect(p, tkLBrace, "expected '{'");
    let b: *Block = bux_alloc(sizeof(Block)) as *Block;
    b.line = parserCurToken(p).line;
    b.column = parserCurToken(p).column;
    b.stmtCount = 0;
    b.firstStmt = null as *Stmt;
    b.lastStmt = null as *Stmt;

    while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
        if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
            discard parserAdvance(p);
            continue;
        }
        let beforePos: int = p.pos;
        let s: *Stmt = parserParseStmt(p);
        // Infinite-loop safeguard: if no progress, advance past current token
        if p.pos == beforePos {
            discard parserAdvance(p);
            continue;
        }
        if s != null as *Stmt {
            s.nextStmt = null as *Stmt;
            if b.firstStmt == null as *Stmt {
                b.firstStmt = s;
                b.lastStmt = s;
            } else {
                b.lastStmt.nextStmt = s;
                b.lastStmt = s;
            }
            b.stmtCount = b.stmtCount + 1;
        }
    }
    discard parserExpect(p, tkRBrace, "expected '}'");
    return b;
}

// ---------------------------------------------------------------------------
// Function parameters
// ---------------------------------------------------------------------------

func parserParseParamList(p: *Parser) -> *Decl {
    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkFunc;
    d.paramCount = 0;

    discard parserExpect(p, tkLParen, "expected '('");
    while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile {
        if d.paramCount >= 9 { break; }
        let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name");
        discard parserExpect(p, tkColon, "expected ':' in parameter");
        let ptype: *TypeExpr = parserParseType(p);

        if d.paramCount == 0 {
            d.param0.name = nameTok.text;
            d.param0.refParamType = ptype;
        } else if d.paramCount == 1 {
            d.param1.name = nameTok.text;
            d.param1.refParamType = ptype;
        } else if d.paramCount == 2 {
            d.param2.name = nameTok.text;
            d.param2.refParamType = ptype;
        } else if d.paramCount == 3 {
            d.param3.name = nameTok.text;
            d.param3.refParamType = ptype;
        } else if d.paramCount == 4 {
            d.param4.name = nameTok.text;
            d.param4.refParamType = ptype;
        } else if d.paramCount == 5 {
            d.param5.name = nameTok.text;
            d.param5.refParamType = ptype;
        } else if d.paramCount == 6 {
            d.param6.name = nameTok.text;
            d.param6.refParamType = ptype;
        } else if d.paramCount == 7 {
            d.param7.name = nameTok.text;
            d.param7.refParamType = ptype;
        } else if d.paramCount == 8 {
            d.param8.name = nameTok.text;
            d.param8.refParamType = ptype;
        }
        d.paramCount = d.paramCount + 1;

        if parserMatch(p, tkComma) { continue; }
        break;
    }
    discard parserExpect(p, tkRParen, "expected ')'");
    return d;
}

// ---------------------------------------------------------------------------
// Declarations
// ---------------------------------------------------------------------------

func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bool) -> *Decl {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    discard parserExpect(p, tkFunc, "expected 'func'");

    let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected function name");
    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkFunc;
    d.line = line;
    d.column = col;
    d.isPublic = isPublic;
    d.isAsync = isAsync;
    d.strValue = nameTok.text;

    // Type params <T, U>
    if parserCheck(p, tkLt) {
        discard parserAdvance(p);
        let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
        d.typeParam0 = tp0.text;
        d.typeParamCount = 1;
        if parserMatch(p, tkComma) {
            let tp1: LexToken = parserExpect(p, tkIdent, "expected type param");
            d.typeParam1 = tp1.text;
            d.typeParamCount = 2;
        }
        discard parserExpect(p, tkGt, "expected '>'");
    }

    // Params
    let params: *Decl = parserParseParamList(p);
    d.paramCount = params.paramCount;
    d.param0 = params.param0;
    d.param1 = params.param1;
    d.param2 = params.param2;
    d.param3 = params.param3;
    d.param4 = params.param4;
    d.param5 = params.param5;
    d.param6 = params.param6;
    d.param7 = params.param7;
    d.param8 = params.param8;

    // Return type
    if parserMatch(p, tkArrow) {
        d.retType = parserParseType(p);
    }

    // Body
    if !isExtern && parserCheck(p, tkLBrace) {
        d.refBody = parserParseBlock(p);
    } else {
        d.refBody = null as *Block;
    }

    return d;
}

func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    discard parserExpect(p, tkStruct, "expected 'struct'");
    let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected struct name");

    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkStruct;
    d.line = line;
    d.column = col;
    d.isPublic = isPublic;
    d.strValue = nameTok.text;

    // Type params
    if parserCheck(p, tkLt) {
        discard parserAdvance(p);
        let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
        d.typeParam0 = tp0.text;
        d.typeParamCount = 1;
        if parserMatch(p, tkComma) {
            let tp1: LexToken = parserExpect(p, tkIdent, "expected type param");
            d.typeParam1 = tp1.text;
            d.typeParamCount = 2;
        }
        discard parserExpect(p, tkGt, "expected '>'");
    }

    discard parserExpect(p, tkLBrace, "expected '{'");
    while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
        if d.fieldCount >= 256 { break; }
        if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
        if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; }
        let beforePos: int = p.pos;
        let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name");
        discard parserExpect(p, tkColon, "expected ':' in struct field");
        let fType: *TypeExpr = parserParseType(p);
        parserMatch(p, tkSemicolon);
        // Infinite-loop safeguard
        if p.pos == beforePos {
            discard parserAdvance(p);
            continue;
        }

        if d.fieldCount == 0 {
            d.field0.name = fName.text;
            d.field0.refFieldType = fType;
        } else if d.fieldCount == 1 {
            d.field1.name = fName.text;
            d.field1.refFieldType = fType;
        } else if d.fieldCount == 2 {
            d.field2.name = fName.text;
            d.field2.refFieldType = fType;
        } else if d.fieldCount == 3 {
            d.field3.name = fName.text;
            d.field3.refFieldType = fType;
        } else if d.fieldCount == 4 {
            d.field4.name = fName.text;
            d.field4.refFieldType = fType;
        } else if d.fieldCount == 5 {
            d.field5.name = fName.text;
            d.field5.refFieldType = fType;
        } else if d.fieldCount == 6 {
            d.field6.name = fName.text;
            d.field6.refFieldType = fType;
        } else if d.fieldCount == 7 {
            d.field7.name = fName.text;
            d.field7.refFieldType = fType;
        } else if d.fieldCount == 8 {
            d.field8.name = fName.text;
            d.field8.refFieldType = fType;
        } else if d.fieldCount == 9 {
            d.field9.name = fName.text;
            d.field9.refFieldType = fType;
        } else if d.fieldCount == 10 {
            d.field10.name = fName.text;
            d.field10.refFieldType = fType;
        } else if d.fieldCount == 11 {
            d.field11.name = fName.text;
            d.field11.refFieldType = fType;
        } else if d.fieldCount == 12 {
            d.field12.name = fName.text;
            d.field12.refFieldType = fType;
        } else if d.fieldCount == 13 {
            d.field13.name = fName.text;
            d.field13.refFieldType = fType;
        } else if d.fieldCount == 14 {
            d.field14.name = fName.text;
            d.field14.refFieldType = fType;
        } else if d.fieldCount == 15 {
            d.field15.name = fName.text;
            d.field15.refFieldType = fType;
        } else if d.fieldCount == 16 {
            d.field16.name = fName.text;
            d.field16.refFieldType = fType;
        } else if d.fieldCount == 17 {
            d.field17.name = fName.text;
            d.field17.refFieldType = fType;
        } else if d.fieldCount == 18 {
            d.field18.name = fName.text;
            d.field18.refFieldType = fType;
        } else if d.fieldCount == 19 {
            d.field19.name = fName.text;
            d.field19.refFieldType = fType;
        } else if d.fieldCount == 20 {
            d.field20.name = fName.text;
            d.field20.refFieldType = fType;
        } else if d.fieldCount == 21 {
            d.field21.name = fName.text;
            d.field21.refFieldType = fType;
        } else if d.fieldCount == 22 {
            d.field22.name = fName.text;
            d.field22.refFieldType = fType;
        } else if d.fieldCount == 23 {
            d.field23.name = fName.text;
            d.field23.refFieldType = fType;
        } else if d.fieldCount == 24 {
            d.field24.name = fName.text;
            d.field24.refFieldType = fType;
        } else if d.fieldCount == 25 {
            d.field25.name = fName.text;
            d.field25.refFieldType = fType;
        } else if d.fieldCount == 26 {
            d.field26.name = fName.text;
            d.field26.refFieldType = fType;
        } else if d.fieldCount == 27 {
            d.field27.name = fName.text;
            d.field27.refFieldType = fType;
        } else if d.fieldCount == 28 {
            d.field28.name = fName.text;
            d.field28.refFieldType = fType;
        } else if d.fieldCount == 29 {
            d.field29.name = fName.text;
            d.field29.refFieldType = fType;
        } else if d.fieldCount == 30 {
            d.field30.name = fName.text;
            d.field30.refFieldType = fType;
        } else if d.fieldCount == 31 {
            d.field31.name = fName.text;
            d.field31.refFieldType = fType;
        } else if d.fieldCount == 32 {
            d.field32.name = fName.text;
            d.field32.refFieldType = fType;
        } else if d.fieldCount == 33 {
            d.field33.name = fName.text;
            d.field33.refFieldType = fType;
        } else if d.fieldCount == 34 {
            d.field34.name = fName.text;
            d.field34.refFieldType = fType;
        } else if d.fieldCount == 35 {
            d.field35.name = fName.text;
            d.field35.refFieldType = fType;
        } else if d.fieldCount == 36 {
            d.field36.name = fName.text;
            d.field36.refFieldType = fType;
        } else if d.fieldCount == 37 {
            d.field37.name = fName.text;
            d.field37.refFieldType = fType;
        } else if d.fieldCount == 38 {
            d.field38.name = fName.text;
            d.field38.refFieldType = fType;
        } else if d.fieldCount == 39 {
            d.field39.name = fName.text;
            d.field39.refFieldType = fType;
        } else if d.fieldCount == 40 {
            d.field40.name = fName.text;
            d.field40.refFieldType = fType;
        } else if d.fieldCount == 41 {
            d.field41.name = fName.text;
            d.field41.refFieldType = fType;
        } else if d.fieldCount == 42 {
            d.field42.name = fName.text;
            d.field42.refFieldType = fType;
        } else if d.fieldCount == 43 {
            d.field43.name = fName.text;
            d.field43.refFieldType = fType;
        } else if d.fieldCount == 44 {
            d.field44.name = fName.text;
            d.field44.refFieldType = fType;
        } else if d.fieldCount == 45 {
            d.field45.name = fName.text;
            d.field45.refFieldType = fType;
        } else if d.fieldCount == 46 {
            d.field46.name = fName.text;
            d.field46.refFieldType = fType;
        } else if d.fieldCount == 47 {
            d.field47.name = fName.text;
            d.field47.refFieldType = fType;
        } else if d.fieldCount == 48 {
            d.field48.name = fName.text;
            d.field48.refFieldType = fType;
        } else if d.fieldCount == 49 {
            d.field49.name = fName.text;
            d.field49.refFieldType = fType;
        } else if d.fieldCount == 50 {
            d.field50.name = fName.text;
            d.field50.refFieldType = fType;
        } else if d.fieldCount == 51 {
            d.field51.name = fName.text;
            d.field51.refFieldType = fType;
        } else if d.fieldCount == 52 {
            d.field52.name = fName.text;
            d.field52.refFieldType = fType;
        } else if d.fieldCount == 53 {
            d.field53.name = fName.text;
            d.field53.refFieldType = fType;
        } else if d.fieldCount == 54 {
            d.field54.name = fName.text;
            d.field54.refFieldType = fType;
        } else if d.fieldCount == 55 {
            d.field55.name = fName.text;
            d.field55.refFieldType = fType;
        } else if d.fieldCount == 56 {
            d.field56.name = fName.text;
            d.field56.refFieldType = fType;
        } else if d.fieldCount == 57 {
            d.field57.name = fName.text;
            d.field57.refFieldType = fType;
        } else if d.fieldCount == 58 {
            d.field58.name = fName.text;
            d.field58.refFieldType = fType;
        } else if d.fieldCount == 59 {
            d.field59.name = fName.text;
            d.field59.refFieldType = fType;
        } else if d.fieldCount == 60 {
            d.field60.name = fName.text;
            d.field60.refFieldType = fType;
        } else if d.fieldCount == 61 {
            d.field61.name = fName.text;
            d.field61.refFieldType = fType;
        } else if d.fieldCount == 62 {
            d.field62.name = fName.text;
            d.field62.refFieldType = fType;
        } else if d.fieldCount == 63 {
            d.field63.name = fName.text;
            d.field63.refFieldType = fType;
        } else if d.fieldCount == 64 {
            d.field64.name = fName.text;
            d.field64.refFieldType = fType;
        } else if d.fieldCount == 65 {
            d.field65.name = fName.text;
            d.field65.refFieldType = fType;
        } else if d.fieldCount == 66 {
            d.field66.name = fName.text;
            d.field66.refFieldType = fType;
        } else if d.fieldCount == 67 {
            d.field67.name = fName.text;
            d.field67.refFieldType = fType;
        } else if d.fieldCount == 68 {
            d.field68.name = fName.text;
            d.field68.refFieldType = fType;
        } else if d.fieldCount == 69 {
            d.field69.name = fName.text;
            d.field69.refFieldType = fType;
        } else if d.fieldCount == 70 {
            d.field70.name = fName.text;
            d.field70.refFieldType = fType;
        } else if d.fieldCount == 71 {
            d.field71.name = fName.text;
            d.field71.refFieldType = fType;
        } else if d.fieldCount == 72 {
            d.field72.name = fName.text;
            d.field72.refFieldType = fType;
        } else if d.fieldCount == 73 {
            d.field73.name = fName.text;
            d.field73.refFieldType = fType;
        } else if d.fieldCount == 74 {
            d.field74.name = fName.text;
            d.field74.refFieldType = fType;
        } else if d.fieldCount == 75 {
            d.field75.name = fName.text;
            d.field75.refFieldType = fType;
        } else if d.fieldCount == 76 {
            d.field76.name = fName.text;
            d.field76.refFieldType = fType;
        } else if d.fieldCount == 77 {
            d.field77.name = fName.text;
            d.field77.refFieldType = fType;
        } else if d.fieldCount == 78 {
            d.field78.name = fName.text;
            d.field78.refFieldType = fType;
        } else if d.fieldCount == 79 {
            d.field79.name = fName.text;
            d.field79.refFieldType = fType;
        } else if d.fieldCount == 80 {
            d.field80.name = fName.text;
            d.field80.refFieldType = fType;
        } else if d.fieldCount == 81 {
            d.field81.name = fName.text;
            d.field81.refFieldType = fType;
        } else if d.fieldCount == 82 {
            d.field82.name = fName.text;
            d.field82.refFieldType = fType;
        } else if d.fieldCount == 83 {
            d.field83.name = fName.text;
            d.field83.refFieldType = fType;
        } else if d.fieldCount == 84 {
            d.field84.name = fName.text;
            d.field84.refFieldType = fType;
        } else if d.fieldCount == 85 {
            d.field85.name = fName.text;
            d.field85.refFieldType = fType;
        } else if d.fieldCount == 86 {
            d.field86.name = fName.text;
            d.field86.refFieldType = fType;
        } else if d.fieldCount == 87 {
            d.field87.name = fName.text;
            d.field87.refFieldType = fType;
        } else if d.fieldCount == 88 {
            d.field88.name = fName.text;
            d.field88.refFieldType = fType;
        } else if d.fieldCount == 89 {
            d.field89.name = fName.text;
            d.field89.refFieldType = fType;
        } else if d.fieldCount == 90 {
            d.field90.name = fName.text;
            d.field90.refFieldType = fType;
        } else if d.fieldCount == 91 {
            d.field91.name = fName.text;
            d.field91.refFieldType = fType;
        } else if d.fieldCount == 92 {
            d.field92.name = fName.text;
            d.field92.refFieldType = fType;
        } else if d.fieldCount == 93 {
            d.field93.name = fName.text;
            d.field93.refFieldType = fType;
        } else if d.fieldCount == 94 {
            d.field94.name = fName.text;
            d.field94.refFieldType = fType;
        } else if d.fieldCount == 95 {
            d.field95.name = fName.text;
            d.field95.refFieldType = fType;
        } else if d.fieldCount == 96 {
            d.field96.name = fName.text;
            d.field96.refFieldType = fType;
        } else if d.fieldCount == 97 {
            d.field97.name = fName.text;
            d.field97.refFieldType = fType;
        } else if d.fieldCount == 98 {
            d.field98.name = fName.text;
            d.field98.refFieldType = fType;
        } else if d.fieldCount == 99 {
            d.field99.name = fName.text;
            d.field99.refFieldType = fType;
        } else if d.fieldCount == 100 {
            d.field100.name = fName.text;
            d.field100.refFieldType = fType;
        } else if d.fieldCount == 101 {
            d.field101.name = fName.text;
            d.field101.refFieldType = fType;
        } else if d.fieldCount == 102 {
            d.field102.name = fName.text;
            d.field102.refFieldType = fType;
        } else if d.fieldCount == 103 {
            d.field103.name = fName.text;
            d.field103.refFieldType = fType;
        } else if d.fieldCount == 104 {
            d.field104.name = fName.text;
            d.field104.refFieldType = fType;
        } else if d.fieldCount == 105 {
            d.field105.name = fName.text;
            d.field105.refFieldType = fType;
        } else if d.fieldCount == 106 {
            d.field106.name = fName.text;
            d.field106.refFieldType = fType;
        } else if d.fieldCount == 107 {
            d.field107.name = fName.text;
            d.field107.refFieldType = fType;
        } else if d.fieldCount == 108 {
            d.field108.name = fName.text;
            d.field108.refFieldType = fType;
        } else if d.fieldCount == 109 {
            d.field109.name = fName.text;
            d.field109.refFieldType = fType;
        } else if d.fieldCount == 110 {
            d.field110.name = fName.text;
            d.field110.refFieldType = fType;
        } else if d.fieldCount == 111 {
            d.field111.name = fName.text;
            d.field111.refFieldType = fType;
        } else if d.fieldCount == 112 {
            d.field112.name = fName.text;
            d.field112.refFieldType = fType;
        } else if d.fieldCount == 113 {
            d.field113.name = fName.text;
            d.field113.refFieldType = fType;
        } else if d.fieldCount == 114 {
            d.field114.name = fName.text;
            d.field114.refFieldType = fType;
        } else if d.fieldCount == 115 {
            d.field115.name = fName.text;
            d.field115.refFieldType = fType;
        } else if d.fieldCount == 116 {
            d.field116.name = fName.text;
            d.field116.refFieldType = fType;
        } else if d.fieldCount == 117 {
            d.field117.name = fName.text;
            d.field117.refFieldType = fType;
        } else if d.fieldCount == 118 {
            d.field118.name = fName.text;
            d.field118.refFieldType = fType;
        } else if d.fieldCount == 119 {
            d.field119.name = fName.text;
            d.field119.refFieldType = fType;
        } else if d.fieldCount == 120 {
            d.field120.name = fName.text;
            d.field120.refFieldType = fType;
        } else if d.fieldCount == 121 {
            d.field121.name = fName.text;
            d.field121.refFieldType = fType;
        } else if d.fieldCount == 122 {
            d.field122.name = fName.text;
            d.field122.refFieldType = fType;
        } else if d.fieldCount == 123 {
            d.field123.name = fName.text;
            d.field123.refFieldType = fType;
        } else if d.fieldCount == 124 {
            d.field124.name = fName.text;
            d.field124.refFieldType = fType;
        } else if d.fieldCount == 125 {
            d.field125.name = fName.text;
            d.field125.refFieldType = fType;
        } else if d.fieldCount == 126 {
            d.field126.name = fName.text;
            d.field126.refFieldType = fType;
        } else if d.fieldCount == 127 {
            d.field127.name = fName.text;
            d.field127.refFieldType = fType;
        } else if d.fieldCount == 128 {
            d.field128.name = fName.text;
            d.field128.refFieldType = fType;
        } else if d.fieldCount == 129 {
            d.field129.name = fName.text;
            d.field129.refFieldType = fType;
        } else if d.fieldCount == 130 {
            d.field130.name = fName.text;
            d.field130.refFieldType = fType;
        } else if d.fieldCount == 131 {
            d.field131.name = fName.text;
            d.field131.refFieldType = fType;
        } else if d.fieldCount == 132 {
            d.field132.name = fName.text;
            d.field132.refFieldType = fType;
        } else if d.fieldCount == 133 {
            d.field133.name = fName.text;
            d.field133.refFieldType = fType;
        } else if d.fieldCount == 134 {
            d.field134.name = fName.text;
            d.field134.refFieldType = fType;
        } else if d.fieldCount == 135 {
            d.field135.name = fName.text;
            d.field135.refFieldType = fType;
        } else if d.fieldCount == 136 {
            d.field136.name = fName.text;
            d.field136.refFieldType = fType;
        } else if d.fieldCount == 137 {
            d.field137.name = fName.text;
            d.field137.refFieldType = fType;
        } else if d.fieldCount == 138 {
            d.field138.name = fName.text;
            d.field138.refFieldType = fType;
        } else if d.fieldCount == 139 {
            d.field139.name = fName.text;
            d.field139.refFieldType = fType;
        } else if d.fieldCount == 140 {
            d.field140.name = fName.text;
            d.field140.refFieldType = fType;
        } else if d.fieldCount == 141 {
            d.field141.name = fName.text;
            d.field141.refFieldType = fType;
        } else if d.fieldCount == 142 {
            d.field142.name = fName.text;
            d.field142.refFieldType = fType;
        } else if d.fieldCount == 143 {
            d.field143.name = fName.text;
            d.field143.refFieldType = fType;
        } else if d.fieldCount == 144 {
            d.field144.name = fName.text;
            d.field144.refFieldType = fType;
        } else if d.fieldCount == 145 {
            d.field145.name = fName.text;
            d.field145.refFieldType = fType;
        } else if d.fieldCount == 146 {
            d.field146.name = fName.text;
            d.field146.refFieldType = fType;
        } else if d.fieldCount == 147 {
            d.field147.name = fName.text;
            d.field147.refFieldType = fType;
        } else if d.fieldCount == 148 {
            d.field148.name = fName.text;
            d.field148.refFieldType = fType;
        } else if d.fieldCount == 149 {
            d.field149.name = fName.text;
            d.field149.refFieldType = fType;
        } else if d.fieldCount == 150 {
            d.field150.name = fName.text;
            d.field150.refFieldType = fType;
        } else if d.fieldCount == 151 {
            d.field151.name = fName.text;
            d.field151.refFieldType = fType;
        } else if d.fieldCount == 152 {
            d.field152.name = fName.text;
            d.field152.refFieldType = fType;
        } else if d.fieldCount == 153 {
            d.field153.name = fName.text;
            d.field153.refFieldType = fType;
        } else if d.fieldCount == 154 {
            d.field154.name = fName.text;
            d.field154.refFieldType = fType;
        } else if d.fieldCount == 155 {
            d.field155.name = fName.text;
            d.field155.refFieldType = fType;
        } else if d.fieldCount == 156 {
            d.field156.name = fName.text;
            d.field156.refFieldType = fType;
        } else if d.fieldCount == 157 {
            d.field157.name = fName.text;
            d.field157.refFieldType = fType;
        } else if d.fieldCount == 158 {
            d.field158.name = fName.text;
            d.field158.refFieldType = fType;
        } else if d.fieldCount == 159 {
            d.field159.name = fName.text;
            d.field159.refFieldType = fType;
        } else if d.fieldCount == 160 {
            d.field160.name = fName.text;
            d.field160.refFieldType = fType;
        } else if d.fieldCount == 161 {
            d.field161.name = fName.text;
            d.field161.refFieldType = fType;
        } else if d.fieldCount == 162 {
            d.field162.name = fName.text;
            d.field162.refFieldType = fType;
        } else if d.fieldCount == 163 {
            d.field163.name = fName.text;
            d.field163.refFieldType = fType;
        } else if d.fieldCount == 164 {
            d.field164.name = fName.text;
            d.field164.refFieldType = fType;
        } else if d.fieldCount == 165 {
            d.field165.name = fName.text;
            d.field165.refFieldType = fType;
        } else if d.fieldCount == 166 {
            d.field166.name = fName.text;
            d.field166.refFieldType = fType;
        } else if d.fieldCount == 167 {
            d.field167.name = fName.text;
            d.field167.refFieldType = fType;
        } else if d.fieldCount == 168 {
            d.field168.name = fName.text;
            d.field168.refFieldType = fType;
        } else if d.fieldCount == 169 {
            d.field169.name = fName.text;
            d.field169.refFieldType = fType;
        } else if d.fieldCount == 170 {
            d.field170.name = fName.text;
            d.field170.refFieldType = fType;
        } else if d.fieldCount == 171 {
            d.field171.name = fName.text;
            d.field171.refFieldType = fType;
        } else if d.fieldCount == 172 {
            d.field172.name = fName.text;
            d.field172.refFieldType = fType;
        } else if d.fieldCount == 173 {
            d.field173.name = fName.text;
            d.field173.refFieldType = fType;
        } else if d.fieldCount == 174 {
            d.field174.name = fName.text;
            d.field174.refFieldType = fType;
        } else if d.fieldCount == 175 {
            d.field175.name = fName.text;
            d.field175.refFieldType = fType;
        } else if d.fieldCount == 176 {
            d.field176.name = fName.text;
            d.field176.refFieldType = fType;
        } else if d.fieldCount == 177 {
            d.field177.name = fName.text;
            d.field177.refFieldType = fType;
        } else if d.fieldCount == 178 {
            d.field178.name = fName.text;
            d.field178.refFieldType = fType;
        } else if d.fieldCount == 179 {
            d.field179.name = fName.text;
            d.field179.refFieldType = fType;
        } else if d.fieldCount == 180 {
            d.field180.name = fName.text;
            d.field180.refFieldType = fType;
        } else if d.fieldCount == 181 {
            d.field181.name = fName.text;
            d.field181.refFieldType = fType;
        } else if d.fieldCount == 182 {
            d.field182.name = fName.text;
            d.field182.refFieldType = fType;
        } else if d.fieldCount == 183 {
            d.field183.name = fName.text;
            d.field183.refFieldType = fType;
        } else if d.fieldCount == 184 {
            d.field184.name = fName.text;
            d.field184.refFieldType = fType;
        } else if d.fieldCount == 185 {
            d.field185.name = fName.text;
            d.field185.refFieldType = fType;
        } else if d.fieldCount == 186 {
            d.field186.name = fName.text;
            d.field186.refFieldType = fType;
        } else if d.fieldCount == 187 {
            d.field187.name = fName.text;
            d.field187.refFieldType = fType;
        } else if d.fieldCount == 188 {
            d.field188.name = fName.text;
            d.field188.refFieldType = fType;
        } else if d.fieldCount == 189 {
            d.field189.name = fName.text;
            d.field189.refFieldType = fType;
        } else if d.fieldCount == 190 {
            d.field190.name = fName.text;
            d.field190.refFieldType = fType;
        } else if d.fieldCount == 191 {
            d.field191.name = fName.text;
            d.field191.refFieldType = fType;
        } else if d.fieldCount == 192 {
            d.field192.name = fName.text;
            d.field192.refFieldType = fType;
        } else if d.fieldCount == 193 {
            d.field193.name = fName.text;
            d.field193.refFieldType = fType;
        } else if d.fieldCount == 194 {
            d.field194.name = fName.text;
            d.field194.refFieldType = fType;
        } else if d.fieldCount == 195 {
            d.field195.name = fName.text;
            d.field195.refFieldType = fType;
        } else if d.fieldCount == 196 {
            d.field196.name = fName.text;
            d.field196.refFieldType = fType;
        } else if d.fieldCount == 197 {
            d.field197.name = fName.text;
            d.field197.refFieldType = fType;
        } else if d.fieldCount == 198 {
            d.field198.name = fName.text;
            d.field198.refFieldType = fType;
        } else if d.fieldCount == 199 {
            d.field199.name = fName.text;
            d.field199.refFieldType = fType;
        } else if d.fieldCount == 200 {
            d.field200.name = fName.text;
            d.field200.refFieldType = fType;
        } else if d.fieldCount == 201 {
            d.field201.name = fName.text;
            d.field201.refFieldType = fType;
        } else if d.fieldCount == 202 {
            d.field202.name = fName.text;
            d.field202.refFieldType = fType;
        } else if d.fieldCount == 203 {
            d.field203.name = fName.text;
            d.field203.refFieldType = fType;
        } else if d.fieldCount == 204 {
            d.field204.name = fName.text;
            d.field204.refFieldType = fType;
        } else if d.fieldCount == 205 {
            d.field205.name = fName.text;
            d.field205.refFieldType = fType;
        } else if d.fieldCount == 206 {
            d.field206.name = fName.text;
            d.field206.refFieldType = fType;
        } else if d.fieldCount == 207 {
            d.field207.name = fName.text;
            d.field207.refFieldType = fType;
        } else if d.fieldCount == 208 {
            d.field208.name = fName.text;
            d.field208.refFieldType = fType;
        } else if d.fieldCount == 209 {
            d.field209.name = fName.text;
            d.field209.refFieldType = fType;
        } else if d.fieldCount == 210 {
            d.field210.name = fName.text;
            d.field210.refFieldType = fType;
        } else if d.fieldCount == 211 {
            d.field211.name = fName.text;
            d.field211.refFieldType = fType;
        } else if d.fieldCount == 212 {
            d.field212.name = fName.text;
            d.field212.refFieldType = fType;
        } else if d.fieldCount == 213 {
            d.field213.name = fName.text;
            d.field213.refFieldType = fType;
        } else if d.fieldCount == 214 {
            d.field214.name = fName.text;
            d.field214.refFieldType = fType;
        } else if d.fieldCount == 215 {
            d.field215.name = fName.text;
            d.field215.refFieldType = fType;
        } else if d.fieldCount == 216 {
            d.field216.name = fName.text;
            d.field216.refFieldType = fType;
        } else if d.fieldCount == 217 {
            d.field217.name = fName.text;
            d.field217.refFieldType = fType;
        } else if d.fieldCount == 218 {
            d.field218.name = fName.text;
            d.field218.refFieldType = fType;
        } else if d.fieldCount == 219 {
            d.field219.name = fName.text;
            d.field219.refFieldType = fType;
        } else if d.fieldCount == 220 {
            d.field220.name = fName.text;
            d.field220.refFieldType = fType;
        } else if d.fieldCount == 221 {
            d.field221.name = fName.text;
            d.field221.refFieldType = fType;
        } else if d.fieldCount == 222 {
            d.field222.name = fName.text;
            d.field222.refFieldType = fType;
        } else if d.fieldCount == 223 {
            d.field223.name = fName.text;
            d.field223.refFieldType = fType;
        } else if d.fieldCount == 224 {
            d.field224.name = fName.text;
            d.field224.refFieldType = fType;
        } else if d.fieldCount == 225 {
            d.field225.name = fName.text;
            d.field225.refFieldType = fType;
        } else if d.fieldCount == 226 {
            d.field226.name = fName.text;
            d.field226.refFieldType = fType;
        } else if d.fieldCount == 227 {
            d.field227.name = fName.text;
            d.field227.refFieldType = fType;
        } else if d.fieldCount == 228 {
            d.field228.name = fName.text;
            d.field228.refFieldType = fType;
        } else if d.fieldCount == 229 {
            d.field229.name = fName.text;
            d.field229.refFieldType = fType;
        } else if d.fieldCount == 230 {
            d.field230.name = fName.text;
            d.field230.refFieldType = fType;
        } else if d.fieldCount == 231 {
            d.field231.name = fName.text;
            d.field231.refFieldType = fType;
        } else if d.fieldCount == 232 {
            d.field232.name = fName.text;
            d.field232.refFieldType = fType;
        } else if d.fieldCount == 233 {
            d.field233.name = fName.text;
            d.field233.refFieldType = fType;
        } else if d.fieldCount == 234 {
            d.field234.name = fName.text;
            d.field234.refFieldType = fType;
        } else if d.fieldCount == 235 {
            d.field235.name = fName.text;
            d.field235.refFieldType = fType;
        } else if d.fieldCount == 236 {
            d.field236.name = fName.text;
            d.field236.refFieldType = fType;
        } else if d.fieldCount == 237 {
            d.field237.name = fName.text;
            d.field237.refFieldType = fType;
        } else if d.fieldCount == 238 {
            d.field238.name = fName.text;
            d.field238.refFieldType = fType;
        } else if d.fieldCount == 239 {
            d.field239.name = fName.text;
            d.field239.refFieldType = fType;
        } else if d.fieldCount == 240 {
            d.field240.name = fName.text;
            d.field240.refFieldType = fType;
        } else if d.fieldCount == 241 {
            d.field241.name = fName.text;
            d.field241.refFieldType = fType;
        } else if d.fieldCount == 242 {
            d.field242.name = fName.text;
            d.field242.refFieldType = fType;
        } else if d.fieldCount == 243 {
            d.field243.name = fName.text;
            d.field243.refFieldType = fType;
        } else if d.fieldCount == 244 {
            d.field244.name = fName.text;
            d.field244.refFieldType = fType;
        } else if d.fieldCount == 245 {
            d.field245.name = fName.text;
            d.field245.refFieldType = fType;
        } else if d.fieldCount == 246 {
            d.field246.name = fName.text;
            d.field246.refFieldType = fType;
        } else if d.fieldCount == 247 {
            d.field247.name = fName.text;
            d.field247.refFieldType = fType;
        } else if d.fieldCount == 248 {
            d.field248.name = fName.text;
            d.field248.refFieldType = fType;
        } else if d.fieldCount == 249 {
            d.field249.name = fName.text;
            d.field249.refFieldType = fType;
        } else if d.fieldCount == 250 {
            d.field250.name = fName.text;
            d.field250.refFieldType = fType;
        } else if d.fieldCount == 251 {
            d.field251.name = fName.text;
            d.field251.refFieldType = fType;
        } else if d.fieldCount == 252 {
            d.field252.name = fName.text;
            d.field252.refFieldType = fType;
        } else if d.fieldCount == 253 {
            d.field253.name = fName.text;
            d.field253.refFieldType = fType;
        } else if d.fieldCount == 254 {
            d.field254.name = fName.text;
            d.field254.refFieldType = fType;
        } else if d.fieldCount == 255 {
            d.field255.name = fName.text;
            d.field255.refFieldType = fType;
        }
        d.fieldCount = d.fieldCount + 1;
    }
    discard parserExpect(p, tkRBrace, "expected '}'");
    return d;
}

func parserParseEnumDecl(p: *Parser, isPublic: bool) -> *Decl {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    discard parserExpect(p, tkEnum, "expected 'enum'");
    let nameTok: LexToken = parserExpect(p, tkIdent, "expected enum name");

    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkEnum;
    d.line = line;
    d.column = col;
    d.isPublic = isPublic;
    d.strValue = nameTok.text;

    discard parserExpect(p, tkLBrace, "expected '{'");
    while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
        if d.variantCount >= 8 { break; }
        if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; }
        let vName: LexToken = parserExpect(p, tkIdent, "expected variant name");

        var v: EnumVariant;
        v.name = vName.text;
        v.fieldCount = 0;
        v.fieldTypeName0 = "";
        v.fieldTypeName1 = "";

        // Optional (Type, Type) data
        if parserMatch(p, tkLParen) {
            let t0: LexToken = parserExpect(p, tkIdent, "expected data type");
            v.fieldTypeName0 = t0.text;
            v.fieldCount = 1;
            if parserMatch(p, tkComma) {
                let t1: LexToken = parserExpect(p, tkIdent, "expected data type");
                v.fieldTypeName1 = t1.text;
                v.fieldCount = 2;
            }
            discard parserExpect(p, tkRParen, "expected ')'");
        }

        if d.variantCount == 0 { d.variant0 = v; }
        else if d.variantCount == 1 { d.variant1 = v; }
        else if d.variantCount == 2 { d.variant2 = v; }
        else if d.variantCount == 3 { d.variant3 = v; }
        else if d.variantCount == 4 { d.variant4 = v; }
        else if d.variantCount == 5 { d.variant5 = v; }
        else if d.variantCount == 6 { d.variant6 = v; }
        else if d.variantCount == 7 { d.variant7 = v; }
        d.variantCount = d.variantCount + 1;

        parserMatch(p, tkComma);
        if parserCheck(p, tkNewLine) { discard parserAdvance(p); }
    }
    discard parserExpect(p, tkRBrace, "expected '}'");
    return d;
}

func parserParseImportDecl(p: *Parser, isPublic: bool) -> *Decl {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    discard parserExpect(p, tkImport, "expected 'import'");

    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkUse;
    d.line = line;
    d.column = col;
    d.isPublic = isPublic;

    // Parse path: Std::Io::PrintLine
    var pathStr: String = "";
    var segCount: int = 0;
    while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon) && parserPeek(p, 1) != tkLBrace) {
        if segCount > 0 {
            discard parserAdvance(p); // ::
            if String_Len(pathStr) > 0 {
                let tmp: *char8 = bux_alloc(256) as *char8;
                // Append to path string (simplified)
                pathStr = String_Concat(pathStr, "::");
            }
        }
        let seg: LexToken = parserExpect(p, tkIdent, "expected module path segment");
        pathStr = String_Concat(pathStr, seg.text);
        segCount = segCount + 1;
    }
    d.usePath = pathStr;

    // Optional ::{name1, name2}
    if parserMatch(p, tkColonColon) && parserCheck(p, tkLBrace) {
        discard parserAdvance(p); // {
        var names: String = "";
        while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
            let n: LexToken = parserExpect(p, tkIdent, "expected import name");
            names = String_Concat(names, n.text);
            names = String_Concat(names, ",");
            if !parserMatch(p, tkComma) { break; }
        }
        discard parserExpect(p, tkRBrace, "expected '}'");
        d.useNames = names;
        d.useKind = 2; // ukMulti
    } else {
        d.useKind = 0; // ukSingle
    }

    parserMatch(p, tkSemicolon);
    return d;
}

func parserParseExternDecl(p: *Parser, isPublic: bool) -> *Decl {
    let line: uint32 = parserCurToken(p).line;
    let col: uint32 = parserCurToken(p).column;
    discard parserExpect(p, tkExtern, "expected 'extern'");

    if parserCheck(p, tkFunc) {
        let d: *Decl = parserParseFuncDecl(p, isPublic, true, false);
        d.kind = dkExternFunc;
        return d;
    }

    let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
    d.kind = dkExternFunc;
    d.line = line;
    d.column = col;
    d.isPublic = isPublic;
    return d;
}

// ---------------------------------------------------------------------------
// Top-level declaration
// ---------------------------------------------------------------------------

func parserParseDecl(p: *Parser) -> *Decl {
    let isPublic: bool = parserMatch(p, tkPub);
    let kind: int = parserPeek(p, 0);

    if kind == tkAsync && parserPeek(p, 1) == tkFunc {
        discard parserAdvance(p); // async
        return parserParseFuncDecl(p, isPublic, false, true);
    }
    if kind == tkFunc {
        return parserParseFuncDecl(p, isPublic, false, false);
    }
    if kind == tkStruct { return parserParseStructDecl(p, isPublic); }
    if kind == tkEnum { return parserParseEnumDecl(p, isPublic); }
    if kind == tkImport { return parserParseImportDecl(p, isPublic); }
    if kind == tkExtern { return parserParseExternDecl(p, isPublic); }

    if kind == tkExtend {
        discard parserAdvance(p);
        let line: uint32 = parserCurToken(p).line;
        let col: uint32 = parserCurToken(p).column;
        let typeName: LexToken = parserExpect(p, tkIdent, "expected type name");

        let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
        d.kind = dkImpl;
        d.line = line;
        d.column = col;
        d.isPublic = isPublic;
        d.strValue = typeName.text;

        // Optional <T>
        if parserCheck(p, tkLt) {
            discard parserAdvance(p);
            let tp0: LexToken = parserExpect(p, tkIdent, "expected type param");
            d.typeParam0 = tp0.text;
            d.typeParamCount = 1;
            discard parserExpect(p, tkGt, "expected '>'");
        }

        discard parserExpect(p, tkLBrace, "expected '{'");
        while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
            if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
            if parserCheck(p, tkFunc) {
                let m: *Decl = parserParseFuncDecl(p, false, false, false);
                // Store method in linked list
                if d.methodCount == 0 {
                    d.childDecl1 = m;
                } else if d.methodCount == 1 {
                    d.childDecl1.childDecl2 = m;
                }
                d.methodCount = d.methodCount + 1;
            } else {
                break;
            }
        }
        discard parserExpect(p, tkRBrace, "expected '}'");
        return d;
    }

    if kind == tkModule {
        discard parserAdvance(p);
        let name: LexToken = parserExpect(p, tkIdent, "expected module name");
        // Parse optional path segments (::Name)
        while parserCheck(p, tkColonColon) {
            discard parserAdvance(p);
            discard parserExpect(p, tkIdent, "expected module path segment");
        }
        let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
        d.kind = dkModule;
        d.strValue = name.text;
        // Parse module body with braces
        if parserCheck(p, tkLBrace) {
            discard parserAdvance(p); // consume {
            var items: *Decl = null as *Decl;
            var lastItem: *Decl = null as *Decl;
            while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
                if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
                    discard parserAdvance(p);
                    continue;
                }
                let beforePos: int = p.pos;
                let item: *Decl = parserParseDecl(p);
                if item != null as *Decl {
                    item.childDecl2 = null as *Decl;
                    if items == null as *Decl {
                        items = item;
                        lastItem = item;
                    } else {
                        lastItem.childDecl2 = item;
                        lastItem = item;
                    }
                }
                // Infinite-loop safeguard: if no progress, skip token
                if p.pos == beforePos {
                    discard parserAdvance(p);
                }
            }
            parserMatch(p, tkRBrace); // consume }
            d.childDecl1 = items; // first item of module
        } else {
            parserMatch(p, tkSemicolon);
        }
        return d;
    }

    if kind == tkConst {
        discard parserAdvance(p);
        let name: LexToken = parserExpect(p, tkIdent, "expected const name");
        discard parserExpect(p, tkColon, "expected ':'");
        let ct: *TypeExpr = parserParseType(p);
        discard parserExpect(p, tkAssign, "expected '='");
        let val: *Expr = parserParseExpr(p);
        parserMatch(p, tkSemicolon);
        let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
        d.kind = dkConst;
        d.strValue = name.text;
        d.constType = ct;
        d.constValue = val;
        return d;
    }

    // Unknown declaration — skip one token and return null
    // (was: skip to newline/}, which was destructive)
    parserEmitDiag(p, parserCurToken(p).line, parserCurToken(p).column, "skipping unknown declaration");
    discard parserAdvance(p);
    return null as *Decl;
}

// ---------------------------------------------------------------------------
// Module parsing
// ---------------------------------------------------------------------------

func Parser_Parse(tokens: *LexToken, tokenCount: int) -> *Module {
    let p: *Parser = bux_alloc(sizeof(Parser)) as *Parser;
    p.tokens = tokens;
    p.tokenCount = tokenCount;
    p.pos = 0;
    p.structInitAllowed = true;
    let diagBuf: *ParserDiag = bux_alloc(256 as uint * sizeof(ParserDiag)) as *ParserDiag;
    p.diags = diagBuf;
    p.diagCount = 0;

    let mod: *Module = bux_alloc(sizeof(Module)) as *Module;
    mod.name = "";
    mod.itemCount = 0;
    mod.firstItem = null as *Decl;

    // Parse declarations until EOF
    while parserPeek(p, 0) != tkEndOfFile {
        if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
            discard parserAdvance(p);
            continue;
        }
        let beforePos: int = p.pos;
        let decl: *Decl = parserParseDecl(p);
        if decl != null as *Decl {
            decl.childDecl2 = mod.firstItem; // push front
            mod.firstItem = decl;
            mod.itemCount = mod.itemCount + 1;
        }
        // Infinite-loop safeguard: if no progress, skip token
        if p.pos == beforePos {
            discard parserAdvance(p);
        }
    }

    return mod;
}

func Parser_DiagCount(p: *Parser) -> int {
    return p.diagCount;
}

func Parser_Free(p: *Parser) {
    bux_free(p.diags as *void);
    bux_free(p as *void);
}

// ---------------------------------------------------------------------------
// Module merging — merge multiple parsed modules into one
// ---------------------------------------------------------------------------

// Add a single declaration to the merged module (push front to linked list)
func parserMergeAddDecl(mod: *Module, decl: *Decl) {
    if decl == null as *Decl { return; }
    // Flatten module declarations: extract inner items
    if decl.kind == dkModule {
        var inner: *Decl = decl.childDecl1;
        while inner != null as *Decl {
            let next: *Decl = inner.childDecl2;
            inner.childDecl2 = mod.firstItem;
            mod.firstItem = inner;
            mod.itemCount = mod.itemCount + 1;
            inner = next;
        }
    } else {
        decl.childDecl2 = mod.firstItem;
        mod.firstItem = decl;
        mod.itemCount = mod.itemCount + 1;
    }
}

func Parser_MergeModules(modules: *void, count: int) -> *Module {
    // This function is kept for future use but not called by Cli_BuildProject
    // which does inline merging for simplicity
    let merged: *Module = bux_alloc(sizeof(Module)) as *Module;
    merged.name = "main";
    merged.path = "";
    merged.itemCount = 0;
    merged.firstItem = null as *Decl;
    return merged;
}
}
