// 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; extern func bux_str_slice(s: String, start: uint, len: uint) -> String; // 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; func parserParsePattern(p: *Parser) -> *Pattern; func parserParseMatchExpr(p: *Parser) -> *Expr; // --------------------------------------------------------------------------- // Parser state // --------------------------------------------------------------------------- struct Parser { tokens: *LexToken, tokenCount: int, pos: int, diagCount: int, diags: *ParserDiag, structInitAllowed: bool, macroTemplateMode: bool, // allows $(…)* in macro! bodies } struct ParserDiag { line: uint32, column: uint32, message: String, severity: int, /* 0=error (fatal), 1=warning (recoverable) */ } // --------------------------------------------------------------------------- // 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 (`Foo`). // Must not treat value comparisons `x < 0` as generics when a later `x > 0` // exists (e.g. multiple match arm guards). 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); // Hard stops: cannot appear inside <...> type args if kind == tkEndOfFile || kind == tkLBrace || kind == tkRBrace || kind == tkSemicolon || kind == tkFatArrow || kind == tkIf || kind == tkElse || kind == tkWhile || kind == tkFor || kind == tkMatch || kind == tkReturn || kind == tkLet || kind == tkVar || kind == tkEq || kind == tkNe || kind == tkLe || kind == tkGe || kind == tkAmpAmp || kind == tkPipePipe || kind == tkAssign { return false; } // Literals / arithmetic ⇒ value expression, not type args if kind == tkIntLiteral || kind == tkFloatLiteral || kind == tkStringLiteral || kind == tkCharLiteral || kind == tkBoolLiteral || kind == tkPlus || kind == tkMinus || kind == tkSlash || kind == tkPercent { 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 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, severity: 1 }; 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, severity: 1 }; 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, severity: 1 }; p.diagCount = p.diagCount + 1; } return tok; } // --------------------------------------------------------------------------- // Type parsing // --------------------------------------------------------------------------- // C-friendly type name from a TypeExpr (named, tuple, pointer, …). func parserTypeExprCName(te: *TypeExpr) -> String { if te == null as *TypeExpr { return "int"; } if te.kind == tekTuple { if !String_Eq(te.typeName, "") { return te.typeName; } return "Tuple_Empty"; } if te.kind == tekPointer || te.kind == tekRef || te.kind == tekMutRef { if te.pointerPointee != null as *TypeExpr { return String_Concat(parserTypeExprCName(te.pointerPointee), "*"); } return "void*"; } if !String_Eq(te.typeName, "") { if String_Eq(te.typeName, "String") || String_Eq(te.typeName, "str") { return "const char*"; } return te.typeName; } return "int"; } func parserParseType(p: *Parser) -> *TypeExpr { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; let kindTok: int = parserPeek(p, 0); // &T / &'a T (shared) and &mut T / &'a mut T (mutable) if kindTok == tkAmp { discard parserAdvance(p); // & var lt: String = ""; // Optional lifetime: &'a or &'a mut if parserCheck(p, tkLifetime) { let ltTok: LexToken = parserCurToken(p); lt = ltTok.text; discard parserAdvance(p); } var isMut: bool = false; // Check for "mut" keyword if parserCheck(p, tkIdent) { let tok: LexToken = parserCurToken(p); if String_Eq(tok.text, "mut") { isMut = true; discard parserAdvance(p); // mut } } let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; if isMut { te.kind = tekMutRef; } else { te.kind = tekRef; } te.line = line; te.column = col; te.refLifetime = lt; te.pointerPointee = parserParseType(p); if te.pointerPointee != null as *TypeExpr { te.typeName = String_Concat(te.pointerPointee.typeName, "*"); } return te; } // *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; } // func(Params) -> Ret if kindTok == tkFunc { discard parserAdvance(p); discard parserExpect(p, tkLParen, "expected '(' after 'func'"); var params: *TypeExprList = null as *TypeExprList; var paramsTail: *TypeExprList = null as *TypeExprList; var count: int = 0; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { let paramTe: *TypeExpr = parserParseType(p); let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList; node.te = paramTe; node.next = null as *TypeExprList; if params == null as *TypeExprList { params = node; } else { paramsTail.next = node; } paramsTail = node; count = count + 1; if parserCheck(p, tkComma) { discard parserAdvance(p); } } discard parserExpect(p, tkRParen, "expected ')' after func params"); var ret: *TypeExpr = null as *TypeExpr; if parserCheck(p, tkArrow) { discard parserAdvance(p); ret = parserParseType(p); } let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; te.kind = tekFunc; te.line = line; te.column = col; te.funcParams = params; te.funcRet = ret; te.funcParamCount = count; return te; } // (T, U, ...) tuple type if kindTok == tkLParen { discard parserAdvance(p); var elems: *TypeExprList = null as *TypeExprList; var elemsTail: *TypeExprList = null as *TypeExprList; var count: int = 0; var typeName: String = "Tuple"; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { let elemTe: *TypeExpr = parserParseType(p); let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList; node.te = elemTe; node.next = null as *TypeExprList; if elems == null as *TypeExprList { elems = node; } else { elemsTail.next = node; } elemsTail = node; count = count + 1; // Build mangled name: Tuple_int_int var part: String = "int"; if elemTe != null as *TypeExpr { if !String_Eq(elemTe.typeName, "") { part = elemTe.typeName; } else if elemTe.kind == tekPointer && elemTe.pointerPointee != null as *TypeExpr { part = String_Concat(elemTe.pointerPointee.typeName, "Ptr"); } } if String_Eq(part, "String") || String_Eq(part, "str") { part = "cstr"; } typeName = String_Concat(typeName, "_"); typeName = String_Concat(typeName, part); if parserCheck(p, tkComma) { discard parserAdvance(p); } else { break; } } discard parserExpect(p, tkRParen, "expected ')' to close tuple type"); if count == 0 { typeName = "Tuple_Empty"; } let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; te.kind = tekTuple; te.line = line; te.column = col; te.tupleElems = elems; te.tupleCount = count; te.typeName = typeName; return te; } // name let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name"); // self / Self -> tekSelf if String_Eq(nameTok.text, "self") || String_Eq(nameTok.text, "Self") { let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; te.kind = tekSelf; te.line = nameTok.line; te.column = nameTok.column; te.typeName = "Self"; return te; } 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; e.callArgs = null as *ExprList; e.callArgCount = 0; e.matchArms = null as *MatchArm; e.matchArmCount = 0; return e; } func parserMakeStringLitExpr(text: String, line: uint32, col: uint32) -> *Expr { let quoted: String = String_Concat(String_Concat("\"", text), "\""); let e: *Expr = parserMakeExpr(ekLiteral, line, col); e.tokKind = tkStringLiteral; e.tokText = quoted; return e; } func parserParseInterpFragment(exprStr: String) -> *Expr { let lex: *Lexer = Lexer_Tokenize(exprStr); var sub: Parser; sub.tokens = lex.tokens; sub.tokenCount = lex.tokenCount; sub.pos = 0; sub.diagCount = 0; sub.diags = null as *ParserDiag; sub.structInitAllowed = true; return parserParseExpr(&sub); } func parserAppendPart(head: *ExprList, tail: *ExprList, e: *Expr) -> *ExprList { // returns new tail; head updated via pointer trick not possible — return pair as side effect on first arg using double pointer? // simpler: just inline in main return tail; } func parserParseStringInterp(p: *Parser, tok: LexToken) -> *Expr { let text: String = tok.text; let tlen: uint = bux_strlen(text); if tlen < 3 as uint { let e: *Expr = parserMakeExpr(ekLiteral, tok.line, tok.column); e.tokKind = tkStringLiteral; e.tokText = text; return e; } if text[0] as int != 102 { let e: *Expr = parserMakeExpr(ekLiteral, tok.line, tok.column); e.tokKind = tkStringLiteral; e.tokText = text; return e; } if text[1] as int != 34 { let e: *Expr = parserMakeExpr(ekLiteral, tok.line, tok.column); e.tokKind = tkStringLiteral; e.tokText = text; return e; } let inner: String = bux_str_slice(text, 2, tlen - 3); let innerLen: uint = bux_strlen(inner); var head: *ExprList = null as *ExprList; var tail: *ExprList = null as *ExprList; var currentText: String = ""; var i: uint = 0; var partCount: int = 0; while i < innerLen { let ch: int = inner[i] as int; var handled: bool = false; if ch == 92 { if i + 1 < innerLen { let nch: int = inner[i + 1] as int; if nch == 123 { currentText = String_Concat(currentText, "{"); i = i + 2; handled = true; } else { if nch == 125 { currentText = String_Concat(currentText, "}"); i = i + 2; handled = true; } } } } if handled { continue; } if ch == 123 { let textPart: *Expr = parserMakeStringLitExpr(currentText, tok.line, tok.column); let textNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; textNode.expr = textPart; textNode.next = null as *ExprList; textNode.argName = ""; if head == null as *ExprList { head = textNode; tail = textNode; } else { tail.next = textNode; tail = textNode; } partCount = partCount + 1; currentText = ""; var j: uint = i + 1; var depth: int = 1; while j < innerLen { if depth <= 0 { break; } let cj: int = inner[j] as int; if cj == 123 { depth = depth + 1; } if cj == 125 { depth = depth - 1; } j = j + 1; } if depth != 0 { parserEmitDiag(p, tok.line, tok.column, "unmatched brace in string interpolation"); let bad: *Expr = parserMakeExpr(ekLiteral, tok.line, tok.column); bad.tokKind = tkStringLiteral; bad.tokText = "\"\""; return bad; } let exprLen: uint = j - i - 2; let exprStr: String = bux_str_slice(inner, i + 1, exprLen); if bux_strlen(exprStr) == 0 { parserEmitDiag(p, tok.line, tok.column, "empty interpolation"); let bad: *Expr = parserMakeExpr(ekLiteral, tok.line, tok.column); bad.tokKind = tkStringLiteral; bad.tokText = "\"\""; return bad; } let frag: *Expr = parserParseInterpFragment(exprStr); let fragNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; fragNode.expr = frag; fragNode.next = null as *ExprList; fragNode.argName = ""; if head == null as *ExprList { head = fragNode; tail = fragNode; } else { tail.next = fragNode; tail = fragNode; } partCount = partCount + 1; i = j; continue; } let one: String = bux_str_slice(inner, i, 1); currentText = String_Concat(currentText, one); i = i + 1; } let lastPart: *Expr = parserMakeStringLitExpr(currentText, tok.line, tok.column); let lastNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; lastNode.expr = lastPart; lastNode.next = null as *ExprList; lastNode.argName = ""; if head == null as *ExprList { head = lastNode; tail = lastNode; } else { tail.next = lastNode; tail = lastNode; } partCount = partCount + 1; if partCount == 1 { return head.expr; } let e: *Expr = parserMakeExpr(ekStringInterp, tok.line, tok.column); e.callArgs = head; e.callArgCount = partCount; return e; } // --------------------------------------------------------------------------- // Primary expressions // --------------------------------------------------------------------------- func parserParsePrimary(p: *Parser) -> *Expr { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } 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); if kind == tkStringLiteral && bux_strlen(tok.text) >= 2 as uint && tok.text[0] as int == 102 && tok.text[1] as int == 34 { return parserParseStringInterp(p, tok); } 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 ) or (a, b, ...) tuple if kind == tkLParen { discard parserAdvance(p); // Empty tuple () if parserCheck(p, tkRParen) { discard parserAdvance(p); let te: *Expr = parserMakeExpr(ekTuple, line, col); te.callArgCount = 0; return te; } let first: *Expr = parserParseExpr(p); if parserCheck(p, tkComma) { // Tuple expression let te: *Expr = parserMakeExpr(ekTuple, line, col); var firstArg: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; firstArg.expr = first; firstArg.next = null as *ExprList; var lastArg: *ExprList = firstArg; var count: int = 1; while parserCheck(p, tkComma) { discard parserAdvance(p); if parserCheck(p, tkRParen) { break; } let elem: *Expr = parserParseExpr(p); let node: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; node.expr = elem; node.next = null as *ExprList; lastArg.next = node; lastArg = node; count = count + 1; } discard parserExpect(p, tkRParen, "expected ')' to close tuple"); te.callArgs = firstArg; te.callArgCount = count; return te; } discard parserExpect(p, tkRParen, "expected ')'"); return first; } // Empty-param closure: `||` is lexed as tkPipePipe (logical-or token). // As a primary it can only mean a zero-param closure: || -> T { ... } if kind == tkPipePipe { return parserParseEmptyClosure(p); } // Closure: |params| -> Ret { body } if kind == tkPipe { return parserParseClosure(p); } // Block expression { stmts; value } if kind == tkLBrace { let e: *Expr = parserMakeExpr(ekBlock, line, col); e.boolValue = false; e.refBlock = parserParseBlock(p); return e; } // unsafe { ... } — unsafe block expression if kind == tkUnsafe { discard parserAdvance(p); let e: *Expr = parserMakeExpr(ekBlock, line, col); e.boolValue = true; // marks this block as unsafe e.refBlock = parserParseBlock(p); return e; } // match expr { arms } if kind == tkMatch { return parserParseMatchExpr(p); } parserEmitDiag(p, line, col, "expected expression"); return parserMakeExpr(ekLiteral, line, col); } // --------------------------------------------------------------------------- // Patterns (for match arms) // --------------------------------------------------------------------------- func parserMakePattern(kind: int, line: uint32, col: uint32) -> *Pattern { let pat: *Pattern = bux_alloc(sizeof(Pattern)) as *Pattern; pat.kind = kind; pat.line = line; pat.column = col; pat.patIdent = ""; pat.patLitKind = 0; pat.patLitText = ""; pat.patRangeInclusive = false; pat.patEnumPath = ""; pat.patStructName = ""; pat.patFieldName = ""; pat.patChild1 = null as *Pattern; pat.patChild2 = null as *Pattern; pat.patArgs = null as *Pattern; pat.patNext = null as *Pattern; pat.patGuardExpr = null as *Expr; return pat; } func parserParsePrimaryPattern(p: *Parser) -> *Pattern { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } let tok: LexToken = parserCurToken(p); let line: uint32 = tok.line; let col: uint32 = tok.column; let kind: int = tok.kind; // _ if kind == tkUnderscore { discard parserAdvance(p); return parserMakePattern(pkWildcard, line, col); } // Literals if kind == tkIntLiteral || kind == tkFloatLiteral || kind == tkStringLiteral || kind == tkCharLiteral || kind == tkBoolLiteral { discard parserAdvance(p); let pat: *Pattern = parserMakePattern(pkLiteral, line, col); pat.patLitKind = kind; pat.patLitText = tok.text; return pat; } // Ident / enum path / true|false as names if kind == tkIdent { discard parserAdvance(p); let name: String = tok.text; if String_Eq(name, "true") || String_Eq(name, "false") { let pat: *Pattern = parserMakePattern(pkLiteral, line, col); pat.patLitKind = tkBoolLiteral; pat.patLitText = name; return pat; } // Enum path: Enum::Variant or Enum::Variant(...) if parserCheck(p, tkColonColon) { var path: String = name; while parserCheck(p, tkColonColon) { discard parserAdvance(p); let seg: LexToken = parserExpectIdentOrKeyword(p, "expected identifier in pattern path"); path = String_Concat(path, "::"); path = String_Concat(path, seg.text); } // Optional (args) for algebraic variants — store as patArgs linked list var enumArgs: *Pattern = null as *Pattern; var lastArg: *Pattern = null as *Pattern; if parserCheck(p, tkLParen) { discard parserAdvance(p); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { let argPat: *Pattern = parserParsePattern(p); if enumArgs == null as *Pattern { enumArgs = argPat; lastArg = argPat; } else { lastArg.patNext = argPat; lastArg = argPat; } if parserCheck(p, tkComma) { discard parserAdvance(p); } else { break; } } discard parserExpect(p, tkRParen, "expected ')' to close enum pattern"); } let pat: *Pattern = parserMakePattern(pkEnum, line, col); pat.patEnumPath = path; pat.patArgs = enumArgs; return pat; } // Struct pattern: Point { x: a, y: b } or shorthand Point { x, y } if parserCheck(p, tkLBrace) { discard parserAdvance(p); var fieldHead: *Pattern = null as *Pattern; var fieldTail: *Pattern = null as *Pattern; while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) { break; } let ftok: LexToken = parserExpectIdentOrKeyword(p, "expected field name in struct pattern"); let fieldName: String = ftok.text; var fieldPat: *Pattern = null as *Pattern; if parserCheck(p, tkColon) { discard parserAdvance(p); fieldPat = parserParsePattern(p); } else { // Shorthand { x } → { x: x } fieldPat = parserMakePattern(pkIdent, line, col); fieldPat.patIdent = fieldName; } fieldPat.patFieldName = fieldName; if fieldHead == null as *Pattern { fieldHead = fieldPat; fieldTail = fieldPat; } else { fieldTail.patNext = fieldPat; fieldTail = fieldPat; } if parserCheck(p, tkComma) { discard parserAdvance(p); } else { break; } } discard parserExpect(p, tkRBrace, "expected '}' to close struct pattern"); let spat: *Pattern = parserMakePattern(pkStruct, line, col); spat.patStructName = name; spat.patArgs = fieldHead; return spat; } // Bare name with (args): Variant(...) treated as single-segment enum if parserCheck(p, tkLParen) { discard parserAdvance(p); var bareArgs: *Pattern = null as *Pattern; var bareLast: *Pattern = null as *Pattern; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { let argPat: *Pattern = parserParsePattern(p); if bareArgs == null as *Pattern { bareArgs = argPat; bareLast = argPat; } else { bareLast.patNext = argPat; bareLast = argPat; } if parserCheck(p, tkComma) { discard parserAdvance(p); } else { break; } } discard parserExpect(p, tkRParen, "expected ')' to close pattern"); let pat: *Pattern = parserMakePattern(pkEnum, line, col); pat.patEnumPath = name; pat.patArgs = bareArgs; return pat; } // Ident binding / catch-all name let pat: *Pattern = parserMakePattern(pkIdent, line, col); pat.patIdent = name; return pat; } // Tuple pattern: (a, b) if kind == tkLParen { discard parserAdvance(p); var head: *Pattern = null as *Pattern; var tail: *Pattern = null as *Pattern; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { let elem: *Pattern = parserParsePattern(p); if head == null as *Pattern { head = elem; tail = elem; } else { tail.patNext = elem; tail = elem; } if parserCheck(p, tkComma) { discard parserAdvance(p); } else { break; } } discard parserExpect(p, tkRParen, "expected ')' to close tuple pattern"); let tpat: *Pattern = parserMakePattern(pkTuple, line, col); tpat.patArgs = head; return tpat; } parserEmitDiag(p, line, col, "expected pattern"); return parserMakePattern(pkWildcard, line, col); } func parserParsePattern(p: *Parser) -> *Pattern { let locTok: LexToken = parserCurToken(p); let line: uint32 = locTok.line; let col: uint32 = locTok.column; let left: *Pattern = parserParsePrimaryPattern(p); // Range pattern: lo..hi or lo..=hi if parserCheck(p, tkDotDot) || parserCheck(p, tkDotDotEqual) { let inclusive: bool = parserCheck(p, tkDotDotEqual); discard parserAdvance(p); let right: *Pattern = parserParsePrimaryPattern(p); let pat: *Pattern = parserMakePattern(pkRange, line, col); pat.patRangeInclusive = inclusive; pat.patChild1 = left; pat.patChild2 = right; // Range can still take a guard: `1..10 if x % 2 == 0` if parserCheck(p, tkIf) { discard parserAdvance(p); let guard: *Expr = parserParseExpr(p); let gpat: *Pattern = parserMakePattern(pkGuarded, line, col); gpat.patChild1 = pat; gpat.patGuardExpr = guard; return gpat; } return pat; } // Guarded pattern: `p if cond` (bindings from p visible in cond) if parserCheck(p, tkIf) { discard parserAdvance(p); let guard: *Expr = parserParseExpr(p); let pat: *Pattern = parserMakePattern(pkGuarded, line, col); pat.patChild1 = left; pat.patGuardExpr = guard; return pat; } return left; } // match subject { pat => body, ... } func parserParseMatchExpr(p: *Parser) -> *Expr { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserAdvance(p); // match p.structInitAllowed = false; let subject: *Expr = parserParseExpr(p); p.structInitAllowed = true; // Allow newline before '{' (needed for `let x = match n \n { ... }`) while parserCheck(p, tkNewLine) { discard parserAdvance(p); } discard parserExpect(p, tkLBrace, "expected '{' to start match body"); var firstArm: *MatchArm = null as *MatchArm; var lastArm: *MatchArm = null as *MatchArm; var armCount: int = 0; 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 armLine: uint32 = parserCurToken(p).line; let armCol: uint32 = parserCurToken(p).column; let mp: int = p.pos; let pat: *Pattern = parserParsePattern(p); discard parserExpect(p, tkFatArrow, "expected '=>' in match arm"); let body: *Expr = parserParseExpr(p); let arm: *MatchArm = bux_alloc(sizeof(MatchArm)) as *MatchArm; arm.line = armLine; arm.column = armCol; arm.pattern = pat; arm.body = body; arm.next = null as *MatchArm; if firstArm == null as *MatchArm { firstArm = arm; lastArm = arm; } else { lastArm.next = arm; lastArm = arm; } armCount = armCount + 1; if parserCheck(p, tkComma) { discard parserAdvance(p); } if p.pos == mp { discard parserAdvance(p); } } discard parserExpect(p, tkRBrace, "expected '}' to close match"); let e: *Expr = parserMakeExpr(ekMatch, line, col); e.child1 = subject; e.matchArms = firstArm; e.matchArmCount = armCount; return e; } // --------------------------------------------------------------------------- // Closure: |params| -> Ret { body } // --------------------------------------------------------------------------- // Zero-param closure when written as `||` (single tkPipePipe token from lexer) func parserParseEmptyClosure(p: *Parser) -> *Expr { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserAdvance(p); // || let e: *Expr = parserMakeExpr(ekClosure, line, col); let params: *Decl = bux_alloc(sizeof(Decl)) as *Decl; params.kind = dkFunc; params.paramCount = 0; e.closureParams = params; if parserCheck(p, tkArrow) { discard parserAdvance(p); e.refType = parserParseType(p); } e.refBlock = parserParseBlock(p); return e; } func parserParseClosure(p: *Parser) -> *Expr { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkPipe, "expected '|' to start closure params"); let e: *Expr = parserMakeExpr(ekClosure, line, col); let params: *Decl = bux_alloc(sizeof(Decl)) as *Decl; params.kind = dkFunc; params.paramCount = 0; // Parse params: name: Type while !parserCheck(p, tkPipe) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } if parserCheck(p, tkPipe) || parserPeek(p, 0) == tkEndOfFile { break; } if params.paramCount >= 9 { break; } let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name in closure"); discard parserExpect(p, tkColon, "expected ':' in closure parameter"); let ptype: *TypeExpr = parserParseType(p); let idx: int = params.paramCount; if idx == 0 { params.param0.name = nameTok.text; params.param0.refParamType = ptype; } else if idx == 1 { params.param1.name = nameTok.text; params.param1.refParamType = ptype; } else if idx == 2 { params.param2.name = nameTok.text; params.param2.refParamType = ptype; } else if idx == 3 { params.param3.name = nameTok.text; params.param3.refParamType = ptype; } else if idx == 4 { params.param4.name = nameTok.text; params.param4.refParamType = ptype; } else if idx == 5 { params.param5.name = nameTok.text; params.param5.refParamType = ptype; } else if idx == 6 { params.param6.name = nameTok.text; params.param6.refParamType = ptype; } else if idx == 7 { params.param7.name = nameTok.text; params.param7.refParamType = ptype; } else if idx == 8 { params.param8.name = nameTok.text; params.param8.refParamType = ptype; } params.paramCount = params.paramCount + 1; if parserMatch(p, tkComma) { continue; } break; } discard parserExpect(p, tkPipe, "expected '|' to close closure params"); e.closureParams = params; // Optional return type: -> Type if parserMatch(p, tkArrow) { e.refType = parserParseType(p); } // Body: { ... } e.refBlock = parserParseBlock(p); return e; } // --------------------------------------------------------------------------- // 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) { var argExpr: *Expr = null as *Expr; var argName: String = ""; // Named argument: name: value if parserPeek(p, 0) == tkIdent && parserPeek(p, 1) == tkColon { let nameTok: LexToken = parserCurToken(p); argName = nameTok.text; discard parserAdvance(p); // ident discard parserAdvance(p); // : argExpr = parserParseExpr(p); } else { argExpr = parserParseExpr(p); } let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; argNode.expr = argExpr; argNode.next = null as *ExprList; argNode.argName = argName; 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(args) or Type { ... } 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 or tuple index expr.0 / expr.1 if kind == tkDot { discard parserAdvance(p); let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; if parserCheck(p, tkIntLiteral) { let idxTok: LexToken = parserCurToken(p); discard parserAdvance(p); let e: *Expr = parserMakeExpr(ekField, line, col); e.child1 = left; e.strValue = String_Concat("_", idxTok.text); left = e; continue; } let name: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); let e: *Expr = parserMakeExpr(ekField, line, col); e.child1 = left; 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 = parserExpectIdentOrKeyword(p, "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; } // ! — macro call name!(args) or unwrap if kind == tkBang { discard parserAdvance(p); let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; if left.kind == ekIdent && parserCheck(p, tkLParen) { discard parserAdvance(p); // ( let e: *Expr = parserMakeExpr(ekMacroCall, line, col); e.strValue = left.strValue; var argCount: int = 0; var firstArg: *ExprList = null as *ExprList; var lastArg: *ExprList = null as *ExprList; // Multi-rep groups: m!(a,b; c,d) — lengths encoded in genericCallee var groupLens: String = ""; var curGroup: int = 0; var nGroups: int = 0; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRParen) { break; } // `;` starts a new arg group if parserMatch(p, tkSemicolon) { if String_Eq(groupLens, "") { groupLens = String_FromInt(curGroup as int64); } else { groupLens = String_Concat(groupLens, String_Concat(";", String_FromInt(curGroup as int64))); } nGroups = nGroups + 1; curGroup = 0; while parserCheck(p, tkNewLine) { discard parserAdvance(p); } continue; } let argExpr: *Expr = parserParseExpr(p); let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; argNode.expr = argExpr; argNode.next = null as *ExprList; argNode.argName = ""; if firstArg == null as *ExprList { firstArg = argNode; lastArg = argNode; } else { lastArg.next = argNode; lastArg = argNode; } argCount = argCount + 1; curGroup = curGroup + 1; while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserMatch(p, tkComma) { continue; } // allow `;` at loop top; otherwise end of args if !parserCheck(p, tkSemicolon) { break; } } // finalize last group if curGroup > 0 || nGroups == 0 { if String_Eq(groupLens, "") { groupLens = String_FromInt(curGroup as int64); } else { groupLens = String_Concat(groupLens, String_Concat(";", String_FromInt(curGroup as int64))); } nGroups = nGroups + 1; } // single group → empty genericCallee (flat match) if nGroups <= 1 { e.genericCallee = ""; } else { e.genericCallee = groupLens; } e.callArgs = firstArg; e.callArgCount = argCount; discard parserExpect(p, tkRParen, "expected ')' to close macro arguments"); left = e; continue; } 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; // Propagate generic type args from the identifier e.genericTypeArg0 = left.genericTypeArg0; e.genericTypeArg1 = left.genericTypeArg1; e.genericTypeArgCount = left.genericTypeArgCount; left = e; continue; } else { break; } } break; } return left; } // --------------------------------------------------------------------------- // Binary expression (precedence climbing) // All binary operators: arithmetic, comparison, logical, bitwise, assignment // --------------------------------------------------------------------------- func parserPrecedence(op: int) -> int { // Assignment operators are parsed by parserParseAssign, not here 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 { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } let tok: LexToken = parserCurToken(p); let line: uint32 = tok.line; let col: uint32 = tok.column; let kind: int = tok.kind; // -expr, !expr, ~expr, *expr, &expr if kind == tkMinus || kind == tkBang || kind == tkTilde || 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 { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } 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); } // --------------------------------------------------------------------------- // Range: lo .. hi or lo ..= hi // --------------------------------------------------------------------------- func parserParseRange(p: *Parser) -> *Expr { var left: *Expr = parserParseBinary(p); if parserCheck(p, tkDotDot) || parserCheck(p, tkDotDotEqual) { let inclusive: bool = parserCheck(p, tkDotDotEqual); let opTok: LexToken = parserAdvance(p); let right: *Expr = parserParseBinary(p); let e: *Expr = parserMakeExpr(ekRange, opTok.line, opTok.column); e.child1 = left; e.child2 = right; e.boolValue = inclusive; return e; } return left; } // --------------------------------------------------------------------------- // Ternary: cond ? then : else // --------------------------------------------------------------------------- func parserParseTernary(p: *Parser) -> *Expr { var left: *Expr = parserParseRange(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; } // --------------------------------------------------------------------------- // Assignment: target = value (right-associative) // --------------------------------------------------------------------------- func parserParseAssign(p: *Parser) -> *Expr { let left: *Expr = parserParseTernary(p); let op: int = parserPeek(p, 0); if op == tkAssign || op == tkPlusAssign || op == tkMinusAssign || op == tkStarAssign || op == tkSlashAssign || op == tkPercentAssign || op == tkAmpAssign || op == tkPipeAssign || op == tkCaretAssign || op == tkShlAssign || op == tkShrAssign { let opTok: LexToken = parserAdvance(p); let right: *Expr = parserParseAssign(p); let e: *Expr = parserMakeExpr(ekAssign, opTok.line, opTok.column); e.intValue = opTok.kind; e.child1 = left; e.child2 = right; return e; } return left; } // --------------------------------------------------------------------------- // Top-level expression // --------------------------------------------------------------------------- func parserParseExpr(p: *Parser) -> *Expr { return parserParseAssign(p); } // --------------------------------------------------------------------------- // Statements // --------------------------------------------------------------------------- func parserParseStmt(p: *Parser) -> *Stmt { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } let tok: LexToken = parserCurToken(p); let line: uint32 = tok.line; let col: uint32 = tok.column; let kind: int = tok.kind; // Macro template: $( stmts… )* if p.macroTemplateMode && kind == tkDollar && parserPeek(p, 1) == tkLParen { discard parserAdvance(p); // $ discard parserAdvance(p); // ( let body: *Block = bux_alloc(sizeof(Block)) as *Block; body.line = line; body.column = col; body.sourceFile = ""; body.stmtCount = 0; body.firstStmt = null as *Stmt; body.lastStmt = null as *Stmt; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRParen) { break; } let inner: *Stmt = parserParseStmt(p); if body.firstStmt == null as *Stmt { body.firstStmt = inner; body.lastStmt = inner; } else { body.lastStmt.nextStmt = inner; body.lastStmt = inner; } body.stmtCount = body.stmtCount + 1; } discard parserExpect(p, tkRParen, "expected ')' to close macro repetition"); discard parserExpect(p, tkStar, "expected '*' after macro repetition"); parserMatch(p, tkSemicolon); let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; s.kind = skMacroRep; s.line = line; s.column = col; s.refStmtBlock = body; s.nextStmt = null as *Stmt; return s; } // 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; } // defer expr if kind == tkDefer { discard parserAdvance(p); let val: *Expr = parserParseExpr(p); parserMatch(p, tkSemicolon); let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; s.kind = skDefer; 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 — expression statement (arms fully parsed) if kind == tkMatch { let matchExpr: *Expr = parserParseMatchExpr(p); let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; s.kind = skExpr; s.line = line; s.column = col; s.child1 = matchExpr; return s; } // switch if kind == tkSwitch { discard parserAdvance(p); p.structInitAllowed = false; let subject: *Expr = parserParseExpr(p); p.structInitAllowed = true; while parserCheck(p, tkNewLine) { discard parserAdvance(p); } discard parserExpect(p, tkLBrace, "expected '{' to start switch body"); var caseBlock: *Block = bux_alloc(sizeof(Block)) as *Block; caseBlock.line = line; caseBlock.column = col; caseBlock.stmtCount = 0; caseBlock.firstStmt = null as *Stmt; caseBlock.lastStmt = null as *Stmt; var defaultBody: *Block = null as *Block; while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; } if parserCheck(p, tkDefault) { discard parserAdvance(p); discard parserExpect(p, tkColon, "expected ':' after default"); let defaultStmt: *Stmt = parserParseStmt(p); defaultBody = bux_alloc(sizeof(Block)) as *Block; defaultBody.line = defaultStmt.line; defaultBody.column = defaultStmt.column; defaultBody.stmtCount = 1; defaultBody.firstStmt = defaultStmt; defaultBody.lastStmt = defaultStmt; defaultStmt.nextStmt = null as *Stmt; continue; } if parserCheck(p, tkCase) { discard parserAdvance(p); let caseVal: *Expr = parserParseExpr(p); discard parserExpect(p, tkColon, "expected ':' after case value"); let caseStmt: *Stmt = parserParseStmt(p); let caseBody: *Block = bux_alloc(sizeof(Block)) as *Block; caseBody.line = caseStmt.line; caseBody.column = caseStmt.column; caseBody.stmtCount = 1; caseBody.firstStmt = caseStmt; caseBody.lastStmt = caseStmt; caseStmt.nextStmt = null as *Stmt; let c: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; c.kind = skExpr; c.line = caseVal.line; c.column = caseVal.column; c.child1 = caseVal; c.refStmtBlock = caseBody; c.nextStmt = null as *Stmt; if caseBlock.firstStmt == null as *Stmt { caseBlock.firstStmt = c; caseBlock.lastStmt = c; } else { caseBlock.lastStmt.nextStmt = c; caseBlock.lastStmt = c; } caseBlock.stmtCount = caseBlock.stmtCount + 1; continue; } discard parserAdvance(p); } discard parserExpect(p, tkRBrace, "expected '}' to close switch"); let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; s.kind = skSwitch; s.line = line; s.column = col; s.child1 = subject; s.refStmtBlock = caseBlock; s.refStmtElse = defaultBody; 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; // Build AST with parserParseStmt (may not consume all tokens) var blockPos: int = p.pos; while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; } let beforePos: int = p.pos; let s: *Stmt = parserParseStmt(p); 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; } } // Reliable token consumption: reset to after { and use depth counter p.pos = blockPos; var depth: int = 1; while depth > 0 && parserPeek(p, 0) != tkEndOfFile { if parserCheck(p, tkLBrace) { depth = depth + 1; } else if parserCheck(p, tkRBrace) { depth = depth - 1; } discard parserAdvance(p); } 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 { while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } if parserCheck(p, tkRParen) || parserPeek(p, 0) == tkEndOfFile { break; } 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); var defExpr: *Expr = null as *Expr; if parserMatch(p, tkAssign) { defExpr = parserParseExpr(p); } if d.paramCount == 0 { d.param0.name = nameTok.text; d.param0.refParamType = ptype; d.param0.defaultExpr = defExpr; } else if d.paramCount == 1 { d.param1.name = nameTok.text; d.param1.refParamType = ptype; d.param1.defaultExpr = defExpr; } else if d.paramCount == 2 { d.param2.name = nameTok.text; d.param2.refParamType = ptype; d.param2.defaultExpr = defExpr; } else if d.paramCount == 3 { d.param3.name = nameTok.text; d.param3.refParamType = ptype; d.param3.defaultExpr = defExpr; } else if d.paramCount == 4 { d.param4.name = nameTok.text; d.param4.refParamType = ptype; d.param4.defaultExpr = defExpr; } else if d.paramCount == 5 { d.param5.name = nameTok.text; d.param5.refParamType = ptype; d.param5.defaultExpr = defExpr; } else if d.paramCount == 6 { d.param6.name = nameTok.text; d.param6.refParamType = ptype; d.param6.defaultExpr = defExpr; } else if d.paramCount == 7 { d.param7.name = nameTok.text; d.param7.refParamType = ptype; d.param7.defaultExpr = defExpr; } else if d.paramCount == 8 { d.param8.name = nameTok.text; d.param8.refParamType = ptype; d.param8.defaultExpr = defExpr; } d.paramCount = d.paramCount + 1; if parserMatch(p, tkComma) { continue; } break; } discard parserExpect(p, tkRParen, "expected ')'"); return d; } // --------------------------------------------------------------------------- // Type parameters: // --------------------------------------------------------------------------- func parserParseTypeParams(p: *Parser, d: *Decl) { // Lifetime params ('a) are accepted and skipped for monomorphization — // they only annotate &/'a T on parameters/returns (stored in TypeExpr.refLifetime). if !parserCheck(p, tkLt) { return; } discard parserAdvance(p); var typeCount: int = 0; var first: bool = true; while !parserCheck(p, tkGt) && parserPeek(p, 0) != tkEndOfFile { if !first { if !parserMatch(p, tkComma) { break; } } first = false; while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkGt) { break; } // Lifetime type param: 'a — parse and ignore for mono slots if parserCheck(p, tkLifetime) { discard parserAdvance(p); // optional trait bound is nonsense for lifetimes; skip : Bound if present if parserMatch(p, tkColon) { discard parserExpect(p, tkIdent, "expected trait bound name"); } continue; } let tpTok: LexToken = parserExpect(p, tkIdent, "expected type param"); if typeCount == 0 { d.typeParam0 = tpTok.text; typeCount = 1; if parserMatch(p, tkColon) { let bound0: LexToken = parserExpect(p, tkIdent, "expected trait bound name"); d.typeParam0Bound = bound0.text; } } else if typeCount == 1 { d.typeParam1 = tpTok.text; typeCount = 2; if parserMatch(p, tkColon) { let bound1: LexToken = parserExpect(p, tkIdent, "expected trait bound name"); d.typeParam1Bound = bound1.text; } } else { // Extra type params beyond 2 — consume and ignore if parserMatch(p, tkColon) { discard parserExpect(p, tkIdent, "expected trait bound name"); } } } d.typeParamCount = typeCount; discard parserExpect(p, tkGt, "expected '>'"); } // --------------------------------------------------------------------------- // 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 parserParseTypeParams(p, d); // 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.fields = bux_alloc(256 as uint * sizeof(StructField)) as *StructField; if d.fields == null as *StructField { PrintLine("ERROR: bux_alloc returned null for fields"); } d.kind = dkStruct; d.line = line; d.column = col; d.isPublic = isPublic; d.strValue = nameTok.text; var fieldCount: int = 0; // Type params parserParseTypeParams(p, d); discard parserExpect(p, tkLBrace, "expected '{'"); while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { if 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"); if fieldCount >= 250 && fieldCount <= 256 { PrintLine(String_Concat("RAW fieldCount=", bux_int_to_str(fieldCount as int64))); PrintLine(String_Concat("RAW fName=", fName.text)); PrintLine(String_Concat("RAW pos=", bux_int_to_str(p.pos as int64))); } 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; } d.fields[fieldCount].name = fName.text; d.fields[fieldCount].refFieldType = fType; fieldCount = fieldCount + 1; } d.fieldCount = fieldCount; 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 >= 9 { 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 payload: (Type, Type) or (Tuple, ...) — full type expressions if parserMatch(p, tkLParen) { let te0: *TypeExpr = parserParseType(p); v.fieldTypeName0 = parserTypeExprCName(te0); v.fieldCount = 1; if parserMatch(p, tkComma) { let te1: *TypeExpr = parserParseType(p); v.fieldTypeName1 = parserTypeExprCName(te1); 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; } else if d.variantCount == 8 { d.variant8 = 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} or ::* if parserMatch(p, tkColonColon) { if parserCheck(p, tkLBrace) { discard parserAdvance(p); // { var names: String = ""; 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 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 if parserCheck(p, tkStar) { discard parserAdvance(p); // * d.useKind = 1; // ukGlob } else { d.useKind = 0; // ukSingle } } 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; } // --------------------------------------------------------------------------- // Interface declaration // --------------------------------------------------------------------------- func parserParseInterfaceDecl(p: *Parser, isPublic: bool) -> *Decl { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkInterface, "expected 'interface'"); let nameTok: LexToken = parserExpect(p, tkIdent, "expected interface name"); discard parserExpect(p, tkLBrace, "expected '{' to start interface body"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkInterface; d.line = line; d.column = col; d.isPublic = isPublic; d.strValue = nameTok.text; var methods: *Decl = null as *Decl; var lastMethod: *Decl = null as *Decl; 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); if methods == null as *Decl { methods = m; lastMethod = m; } else { lastMethod.childDecl2 = m; lastMethod = m; } d.methodCount = d.methodCount + 1; } else { break; } } d.childDecl1 = methods; discard parserExpect(p, tkRBrace, "expected '}' to close interface"); return d; } // --------------------------------------------------------------------------- // macro! name { ($x:expr, …) => { template } … } // --------------------------------------------------------------------------- func parserSetMacroFragName(d: *Decl, idx: int, name: String) { if idx == 0 { d.param0.name = name; } else if idx == 1 { d.param1.name = name; } else if idx == 2 { d.param2.name = name; } else if idx == 3 { d.param3.name = name; } else if idx == 4 { d.param4.name = name; } else if idx == 5 { d.param5.name = name; } else if idx == 6 { d.param6.name = name; } else if idx == 7 { d.param7.name = name; } else if idx == 8 { d.param8.name = name; } } func parserParseMacroDecl(p: *Parser, isPublic: bool) -> *Decl { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkMacro, "expected 'macro'"); discard parserExpect(p, tkBang, "expected '!' after macro"); let nameTok: LexToken = parserExpect(p, tkIdent, "expected macro name"); while parserCheck(p, tkNewLine) { discard parserAdvance(p); } discard parserExpect(p, tkLBrace, "expected '{' to start macro body"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkMacro; d.line = line; d.column = col; d.isPublic = isPublic; d.strValue = nameTok.text; d.childDecl1 = null as *Decl; d.childDecl2 = null as *Decl; var firstRule: *Decl = null as *Decl; var lastRule: *Decl = null as *Decl; var ruleCount: int = 0; while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) { break; } let rline: uint32 = parserCurToken(p).line; let rcol: uint32 = parserCurToken(p).column; discard parserExpect(p, tkLParen, "expected '(' to start macro pattern"); let rule: *Decl = bux_alloc(sizeof(Decl)) as *Decl; rule.kind = dkMacro; rule.line = rline; rule.column = rcol; rule.strValue = ""; rule.paramCount = 0; rule.childDecl2 = null as *Decl; // useNames encodes: "expr" | "ident" | "tt" | "rep:expr," | "rep:expr+expr," (compound) // Multiple pattern elements joined by `;` → multi-rep groups at call site var kindsEnc: String = ""; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRParen) { break; } if parserMatch(p, tkSemicolon) { continue; } // $( $a:kind , $b:kind ),* if parserCheck(p, tkDollar) && parserPeek(p, 1) == tkLParen { discard parserAdvance(p); // $ discard parserAdvance(p); // ( var repNames: String = ""; var repKinds: String = ""; var nIn: int = 0; while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRParen) { break; } let fragTok: LexToken = parserExpect(p, tkIdent, "expected $name fragment"); if !String_StartsWith(fragTok.text, "$") { parserEmitDiag(p, fragTok.line, fragTok.column, "macro fragment must start with '$'"); } discard parserExpect(p, tkColon, "expected ':' after fragment name"); let kindTok: LexToken = parserExpect(p, tkIdent, "expected fragment kind"); var kname: String = kindTok.text; if String_Eq(kname, "lit") { kname = "literal"; } if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt") || String_Eq(kname, "literal") || String_Eq(kname, "block")) { kname = "expr"; } if nIn == 0 { repNames = fragTok.text; repKinds = kname; } else { repNames = String_Concat(repNames, String_Concat("+", fragTok.text)); repKinds = String_Concat(repKinds, String_Concat("+", kname)); } if rule.paramCount < 9 { parserSetMacroFragName(rule, rule.paramCount, fragTok.text); rule.paramCount = rule.paramCount + 1; } nIn = nIn + 1; while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if !parserMatch(p, tkComma) { break; } } discard parserExpect(p, tkRParen, "expected ')' after repeated fragment"); var sep: String = ""; if parserMatch(p, tkComma) { sep = ","; } discard parserExpect(p, tkStar, "expected '*' after macro repetition"); let enc: String = String_Concat("rep:", String_Concat(repKinds, sep)); // mark compound count via leading digit in typeParam0 of rule (hack: use isDrop) if rule.paramCount > 0 { // store chunk size on last param via isVariadic false; use methodCount as chunk // Encode: kindsEnc entry includes chunk after @ let enc2: String = String_Concat(enc, String_Concat("@", String_FromInt(nIn))); if String_Eq(kindsEnc, "") { kindsEnc = enc2; } else { kindsEnc = String_Concat(kindsEnc, String_Concat(";", enc2)); } } while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserMatch(p, tkSemicolon) { continue; } if parserMatch(p, tkComma) { continue; } break; } else { let fragTok: LexToken = parserExpect(p, tkIdent, "expected $name fragment"); if !String_StartsWith(fragTok.text, "$") { parserEmitDiag(p, fragTok.line, fragTok.column, "macro fragment must start with '$'"); } discard parserExpect(p, tkColon, "expected ':' after fragment name"); let kindTok: LexToken = parserExpect(p, tkIdent, "expected fragment kind"); var kname: String = kindTok.text; if String_Eq(kname, "lit") { kname = "literal"; } if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt") || String_Eq(kname, "literal") || String_Eq(kname, "block")) { kname = "expr"; } if rule.paramCount < 9 { parserSetMacroFragName(rule, rule.paramCount, fragTok.text); rule.paramCount = rule.paramCount + 1; } if String_Eq(kindsEnc, "") { kindsEnc = kname; } else { kindsEnc = String_Concat(kindsEnc, String_Concat(";", kname)); } while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserMatch(p, tkComma) { continue; } if parserMatch(p, tkSemicolon) { continue; } break; } } rule.useNames = kindsEnc; discard parserExpect(p, tkRParen, "expected ')' to close macro pattern"); while parserCheck(p, tkNewLine) { discard parserAdvance(p); } discard parserExpect(p, tkFatArrow, "expected '=>' after macro pattern"); while parserCheck(p, tkNewLine) { discard parserAdvance(p); } let savedTpl: bool = p.macroTemplateMode; p.macroTemplateMode = true; rule.refBody = parserParseBlock(p); p.macroTemplateMode = savedTpl; if firstRule == null as *Decl { firstRule = rule; lastRule = rule; } else { lastRule.childDecl2 = rule; lastRule = rule; } ruleCount = ruleCount + 1; while parserCheck(p, tkNewLine) || parserCheck(p, tkComma) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } } discard parserExpect(p, tkRBrace, "expected '}' to close macro"); d.childDecl1 = firstRule; d.methodCount = ruleCount; if ruleCount == 0 { parserEmitDiag(p, line, col, "macro has no rules"); } return d; } // Top-level declaration // --------------------------------------------------------------------------- func parserParseDecl(p: *Parser) -> *Decl { // Skip newlines before declaration (matching bootstrap behavior) while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } let isPublic: bool = parserMatch(p, tkPub); // Parse stacked @[Checked] / @[Drop] / @[Release] attributes var isChecked: int = 0; var isDrop: int = 0; var isRelease: int = 0; while true { while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } if !parserCheck(p, tkAt) { break; } discard parserAdvance(p); // @ if parserCheck(p, tkLBracket) { discard parserAdvance(p); // [ if parserCheck(p, tkIdent) { let attrName: LexToken = parserCurToken(p); if String_Eq(attrName.text, "Checked") { isChecked = 1; } if String_Eq(attrName.text, "Drop") { isDrop = 1; } if String_Eq(attrName.text, "Release") { isRelease = 1; } discard parserAdvance(p); // attribute name } if parserCheck(p, tkRBracket) { discard parserAdvance(p); // ] } } } while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); } let kind: int = parserPeek(p, 0); if kind == tkAsync && parserPeek(p, 1) == tkFunc { discard parserAdvance(p); // async let d: *Decl = parserParseFuncDecl(p, isPublic, false, true); d.isChecked = isChecked; d.isRelease = isRelease; return d; } if kind == tkConst && parserPeek(p, 1) == tkFunc { discard parserAdvance(p); // const let d: *Decl = parserParseFuncDecl(p, isPublic, false, false); d.isConst = 1; d.isChecked = isChecked; d.isRelease = isRelease; return d; } if kind == tkFunc { let d: *Decl = parserParseFuncDecl(p, isPublic, false, false); d.isChecked = isChecked; d.isRelease = isRelease; return d; } if kind == tkStruct { let d: *Decl = parserParseStructDecl(p, isPublic); d.isDrop = isDrop; return d; } if kind == tkEnum { return parserParseEnumDecl(p, isPublic); } if kind == tkImport { return parserParseImportDecl(p, isPublic); } if kind == tkExtern { return parserParseExternDecl(p, isPublic); } if kind == tkInterface { return parserParseInterfaceDecl(p, isPublic); } if kind == tkMacro { return parserParseMacroDecl(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 parserParseTypeParams(p, d); // Optional 'for InterfaceName' if parserMatch(p, tkFor) { let ifaceName: LexToken = parserExpect(p, tkIdent, "expected interface name"); d.strValue2 = ifaceName.text; } discard parserExpect(p, tkLBrace, "expected '{'"); var methods: *Decl = null as *Decl; var lastMethod: *Decl = null as *Decl; 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); if methods == null as *Decl { methods = m; lastMethod = m; } else { lastMethod.childDecl2 = m; lastMethod = m; } d.methodCount = d.methodCount + 1; } else { break; } } d.childDecl1 = methods; 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); } } discard parserExpect(p, tkRBrace, "expected '}' to close module"); 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; p.macroTemplateMode = false; 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); } } /* Print fatal parser diagnostics (severity == 0) or if nothing valid was parsed */ if p.diagCount > 0 && mod.itemCount == 0 { var di: int = 0; while di < p.diagCount { let d: ParserDiag = p.diags[di]; Print("error: "); PrintLine(d.message); Print(" --> :"); PrintInt(d.line as int64); Print(":"); PrintInt(d.column as int64); PrintLine(""); Print(" |"); PrintLine(""); Print(" "); PrintInt(d.line as int64); Print(" | "); PrintLine(""); Print(" | "); var sp: uint32 = 0; while sp < d.column - 1 && sp < 120 { Print(" "); sp = sp + 1; } PrintLine("^"); di = di + 1; } } return mod; } }