// 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; // --------------------------------------------------------------------------- // 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; } 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); 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) { 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); } 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 "String*" for String pointer, else "int*" if te.pointerPointee != null as *TypeExpr { if String_Eq(te.pointerPointee.typeName, "String") { te.typeName = "String*"; } else if String_Eq(te.pointerPointee.typeName, "int") { te.typeName = "int*"; } else { te.typeName = "int*"; } } 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 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; 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; 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); let e: *Expr = parserMakeExpr(ekSizeOf, line, col); e.refType = parserParseType(p); 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; } // -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 = parserParsePrimary(p); return e; } parserEmitDiag(p, line, col, "expected expression"); return parserMakeExpr(ekLiteral, line, col); } // --------------------------------------------------------------------------- // Postfix: call, index, field access, as, is, ? // --------------------------------------------------------------------------- func parserParsePostfix(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 arguments (up to 8) // child2 = first arg, child3 = second arg, extra = rest if !parserCheck(p, tkRParen) { e.child2 = parserParseExpr(p); if parserMatch(p, tkComma) { e.child3 = parserParseExpr(p); // Consume any remaining arguments (we only store 2) while parserMatch(p, tkComma) { discard parserParseExpr(p); } } } discard parserExpect(p, tkRParen, "expected ')'"); left = e; 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; } // 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; } // 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 parserParseBinary(p: *Parser) -> *Expr { var left: *Expr = parserParsePostfix(p); while parserIsBinaryOp(parserPeek(p, 0)) { let opTok: LexToken = parserAdvance(p); let line: uint32 = opTok.line; let col: uint32 = opTok.column; let right: *Expr = parserParsePostfix(p); let e: *Expr = parserMakeExpr(ekBinary, line, col); e.intValue = opTok.kind; e.child1 = left; e.child2 = right; left = e; } return left; } // --------------------------------------------------------------------------- // 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); } discard parserExpect(p, tkAssign, "expected '=' in let/var statement"); let init: *Expr = 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; } // 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; 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 >= 6 { 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; } 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) -> *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.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 '>'"); } // 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; // 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 >= 64 { 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; } 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; // 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; } 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)) { 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); 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 == tkFunc { return parserParseFuncDecl(p, isPublic, 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 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); // Store method in linked list if d.methodCount == 0 { d.childDecl1 = m; } else if d.methodCount == 1 { d.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; } // Skip unknown declarations while parserPeek(p, 0) != tkEndOfFile && parserPeek(p, 0) != tkNewLine && parserPeek(p, 0) != tkRBrace { 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 decl: *Decl = parserParseDecl(p); if decl != null as *Decl { decl.childDecl2 = mod.firstItem; // push front mod.firstItem = decl; mod.itemCount = mod.itemCount + 1; } } 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; } }