diff --git a/README.md b/README.md index 3e055e9..940a84a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # Bux Programming Language -> **Status:** Self-hosting phase — `buxc2` compiles `.bux` → QBE SSA → native binary. Bootstrap (`buxc`, Nim) maintains backward compatibility via C transpiler. +![Bux Language](bux-lang-01.jpeg) -Bux is a fast, compiled, strongly-typed systems programming language. Features a QBE backend for native code generation, raw multi-line strings, gradual ownership, async/await, generics, algebraic enums, and a package manager. +> **Status:** Self-hosting phase — `buxc2` compiles `.bux` → C → native binary. Bootstrap (`buxc`, Nim) builds the self-hosted compiler. + +Bux is a fast, compiled, strongly-typed systems programming language. Features a C backend for native code generation, raw multi-line strings, gradual ownership, async/await, generics, algebraic enums, and a package manager. --- @@ -148,7 +150,7 @@ func Main() -> int { | **Interfaces** | `interface` + `extend` for trait-like behavior | | **Error Handling** | `Result`, `Option`, and the `?` operator | | **Standard Library** | `Io`, `Array`, `String`, `Map`, `Fs`, `Mem`, `Set`, `Path`, `Math`, `Task`, `Channel` | -| **Backend** | QBE native codegen (self-hosting), C transpiler (bootstrap) | +| **Backend** | C transpiler (self-hosting + bootstrap) | | **Strings** | Raw multi-line backtick strings (`...`), C-string interop | | **Gradual Ownership** | `@[Checked]` + `&T`/`&mut T` borrow checking | | **Async/Await** | `async func`, `spawn`, `.await` with stackful coroutines | @@ -189,7 +191,7 @@ bux/ # Build bootstrap compiler (Nim → C) make build -# Build self-hosted compiler (Bux → QBE SSA → native) +# Build self-hosted compiler (Bux → C → native) make selfhost # Run all tests diff --git a/_selfhost/src/ast.bux b/_selfhost/src/ast.bux index 60b7a03..c8369d7 100644 --- a/_selfhost/src/ast.bux +++ b/_selfhost/src/ast.bux @@ -96,6 +96,11 @@ const ekMatch: int = 22; const ekSpawn: int = 24; const ekAwait: int = 25; +struct ExprList { + expr: *Expr, + next: *ExprList, +} + struct Expr { kind: int, line: uint32, @@ -121,6 +126,9 @@ struct Expr { // Struct init fields structName: String, structFieldCount: int, + // Call arguments (linked list for multi-arg support) + callArgs: *ExprList, + callArgCount: int, } // --------------------------------------------------------------------------- @@ -241,6 +249,9 @@ struct Decl { param3: Param, param4: Param, param5: Param, + param6: Param, + param7: Param, + param8: Param, retType: *TypeExpr, // Body refBody: *Block, @@ -361,7 +372,8 @@ func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr { child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, refType: null as *TypeExpr, refBlock: null as *Block, genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0, - structName: "", structFieldCount: 0 }; + structName: "", structFieldCount: 0, + callArgs: null as *ExprList, callArgCount: 0 }; } func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr { diff --git a/_selfhost/src/c_backend.bux b/_selfhost/src/c_backend.bux index c576288..958d8b8 100644 --- a/_selfhost/src/c_backend.bux +++ b/_selfhost/src/c_backend.bux @@ -134,12 +134,29 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) { if kind == hCall { StringBuilder_Append(&cbe.sb, node.strValue); StringBuilder_Append(&cbe.sb, "("); + var needsComma: bool = false; if node.child1 != null as *HirNode { CBE_EmitExpr(cbe, node.child1); + needsComma = true; } if node.child2 != null as *HirNode { - StringBuilder_Append(&cbe.sb, ", "); + if needsComma { + StringBuilder_Append(&cbe.sb, ", "); + } CBE_EmitExpr(cbe, node.child2); + needsComma = true; + } + // Emit extra args from linked list + var ai: int = 0; + var curExtra: *HirArgList = node.extraData as *HirArgList; + while ai < node.extraCount { + if needsComma { + StringBuilder_Append(&cbe.sb, ", "); + } + CBE_EmitExpr(cbe, curExtra.node); + needsComma = true; + curExtra = curExtra.next; + ai = ai + 1; } StringBuilder_Append(&cbe.sb, ")"); return; @@ -414,6 +431,9 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) { if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; } if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; } if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; } + if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; } + if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; } + if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; } // Emit type if String_Eq(ptype, "String") || String_Eq(ptype, "str") { StringBuilder_Append(&cbe.sb, "String "); @@ -474,6 +494,9 @@ func CBE_FuncHasGeneric(f: *HirFunc) -> bool { if pi == 3 { ptype = f.param3.typeName; } if pi == 4 { ptype = f.param4.typeName; } if pi == 5 { ptype = f.param5.typeName; } + if pi == 6 { ptype = f.param6.typeName; } + if pi == 7 { ptype = f.param7.typeName; } + if pi == 8 { ptype = f.param8.typeName; } if CBE_IsGenericTypeName(ptype) { return true; } pi = pi + 1; } diff --git a/_selfhost/src/cli.bux b/_selfhost/src/cli.bux index 5b422f0..d06db7c 100644 --- a/_selfhost/src/cli.bux +++ b/_selfhost/src/cli.bux @@ -400,8 +400,13 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int { Print("DEBUG: MergeFileInto "); PrintLine(path); + if !FileExists(path) { + Print("Error: stdlib file not found: "); + PrintLine(path); + return 0; + } let source: String = ReadFile(path); - if String_Eq(source, "") { return 0; } + if source == null as String || String_Eq(source, "") { return 0; } let lex: *Lexer = Lexer_Tokenize(source); if Lexer_DiagCount(lex) > 0 { return 0; } let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); diff --git a/_selfhost/src/hir.bux b/_selfhost/src/hir.bux index 3b3667d..2f37e06 100644 --- a/_selfhost/src/hir.bux +++ b/_selfhost/src/hir.bux @@ -34,6 +34,15 @@ const hMatch: int = 28; const hSpawn: int = 29; const hAwait: int = 30; +// --------------------------------------------------------------------------- +// HirArgList — linked list for call arguments beyond 2 +// --------------------------------------------------------------------------- + +struct HirArgList { + node: *HirNode, + next: *HirArgList, +} + // --------------------------------------------------------------------------- // HirNode — unified struct with tagged union pattern // --------------------------------------------------------------------------- @@ -76,6 +85,9 @@ struct HirFunc { param3: HirParam; param4: HirParam; param5: HirParam; + param6: HirParam; + param7: HirParam; + param8: HirParam; retTypeKind: int; retTypeName: String; body: *HirNode; diff --git a/_selfhost/src/hir_lower.bux b/_selfhost/src/hir_lower.bux index 811403a..d783a37 100644 --- a/_selfhost/src/hir_lower.bux +++ b/_selfhost/src/hir_lower.bux @@ -180,11 +180,34 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { // Lower receiver as first argument let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1); n.child1 = recv; - // Lower remaining arguments - if expr.child2 != null as *Expr { - let extra: *HirNode = Lcx_LowerExpr(ctx, expr.child2); - // Chain extra args after receiver via child2/child3 - n.child2 = extra; + // Lower remaining arguments from linked list + var arg: *ExprList = expr.callArgs; + var argIdx: int = 0; + while arg != null as *ExprList { + let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); + if argIdx == 0 { + n.child2 = lowered; + } else if argIdx == 1 { + // Third argument — start linked list + let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + firstExtra.node = lowered; + firstExtra.next = null as *HirArgList; + n.extraData = firstExtra as *void; + n.extraCount = 1; + } else { + // Additional args — append to linked list + var cur: *HirArgList = n.extraData as *HirArgList; + while cur.next != null as *HirArgList { + cur = cur.next; + } + let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + newNode.node = lowered; + newNode.next = null as *HirArgList; + cur.next = newNode; + n.extraCount = n.extraCount + 1; + } + arg = arg.next; + argIdx = argIdx + 1; } return n; } @@ -192,12 +215,36 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { n.strValue = expr.child1.strValue; } - // Lower arguments - if expr.child2 != null as *Expr { - n.child1 = Lcx_LowerExpr(ctx, expr.child2); - } - if expr.child3 != null as *Expr { - n.child2 = Lcx_LowerExpr(ctx, expr.child3); + // Lower arguments from linked list + var arg: *ExprList = expr.callArgs; + var argIdx: int = 0; + while arg != null as *ExprList { + let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); + if argIdx == 0 { + n.child1 = lowered; + } else if argIdx == 1 { + n.child2 = lowered; + } else if argIdx == 2 { + // Third argument — start linked list + let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + firstExtra.node = lowered; + firstExtra.next = null as *HirArgList; + n.extraData = firstExtra as *void; + n.extraCount = 1; + } else { + // Additional args — append to linked list + var cur: *HirArgList = n.extraData as *HirArgList; + while cur.next != null as *HirArgList { + cur = cur.next; + } + let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + newNode.node = lowered; + newNode.next = null as *HirArgList; + cur.next = newNode; + n.extraCount = n.extraCount + 1; + } + arg = arg.next; + argIdx = argIdx + 1; } return n; } @@ -359,6 +406,9 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { sym.name = stmt.strValue; sym.typeKind = alloca.typeKind; sym.typeName = alloca.typeName; + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; discard Scope_Define(ctx.scope, sym); // store the init value n.kind = hStore; @@ -495,6 +545,9 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { Lcx_LowerParam(&f.param3, &decl.param3); Lcx_LowerParam(&f.param4, &decl.param4); Lcx_LowerParam(&f.param5, &decl.param5); + Lcx_LowerParam(&f.param6, &decl.param6); + Lcx_LowerParam(&f.param7, &decl.param7); + Lcx_LowerParam(&f.param8, &decl.param8); if decl.retType != null as *TypeExpr { f.retTypeName = decl.retType.typeName; @@ -514,12 +567,18 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { else if pi == 3 { p = &decl.param3; } else if pi == 4 { p = &decl.param4; } else if pi == 5 { p = &decl.param5; } + else if pi == 6 { p = &decl.param6; } + else if pi == 7 { p = &decl.param7; } + else if pi == 8 { p = &decl.param8; } if p != null as *Param && p.refParamType != null as *TypeExpr { var sym: Symbol; sym.kind = skVar; sym.name = p.name; sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); sym.typeName = p.refParamType.typeName; + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; discard Scope_Define(ctx.scope, sym); } pi = pi + 1; diff --git a/_selfhost/src/lexer.bux b/_selfhost/src/lexer.bux index 7fe64b1..0c5f8e6 100644 --- a/_selfhost/src/lexer.bux +++ b/_selfhost/src/lexer.bux @@ -711,7 +711,7 @@ func Lexer_GetToken(lex: *Lexer, index: int) -> LexToken { if index >= 0 && index < lex.tokenCount { return lex.tokens[index]; } - var empty: LexToken; + var empty: LexToken = LexToken { kind: tkEndOfFile, text: "", line: 0, column: 0 }; return empty; } diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux index e05e678..cedcb4e 100644 --- a/_selfhost/src/parser.bux +++ b/_selfhost/src/parser.bux @@ -214,6 +214,8 @@ func parserMakeExpr(kind: int, line: uint32, col: uint32) -> *Expr { e.genericTypeArgCount = 0; e.structName = ""; e.structFieldCount = 0; + e.callArgs = null as *ExprList; + e.callArgCount = 0; return e; } @@ -337,18 +339,27 @@ func parserParsePostfix(p: *Parser) -> *Expr { 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); - } + // Parse all arguments into linked list + var argCount: int = 0; + var firstArg: *ExprList = null as *ExprList; + var lastArg: *ExprList = null as *ExprList; + while !parserCheck(p, tkRParen) { + let argExpr: *Expr = parserParseExpr(p); + let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; + argNode.expr = argExpr; + argNode.next = null as *ExprList; + if firstArg == null as *ExprList { + firstArg = argNode; + lastArg = argNode; + } else { + lastArg.next = argNode; + lastArg = argNode; } + argCount = argCount + 1; + if !parserMatch(p, tkComma) { break; } } + e.callArgs = firstArg; + e.callArgCount = argCount; discard parserExpect(p, tkRParen, "expected ')'"); left = e; continue; @@ -842,7 +853,7 @@ func parserParseParamList(p: *Parser) -> *Decl { discard parserExpect(p, tkLParen, "expected '('"); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { - if d.paramCount >= 6 { 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); @@ -865,6 +876,15 @@ func parserParseParamList(p: *Parser) -> *Decl { } else if d.paramCount == 5 { d.param5.name = nameTok.text; d.param5.refParamType = ptype; + } else if d.paramCount == 6 { + d.param6.name = nameTok.text; + d.param6.refParamType = ptype; + } else if d.paramCount == 7 { + d.param7.name = nameTok.text; + d.param7.refParamType = ptype; + } else if d.paramCount == 8 { + d.param8.name = nameTok.text; + d.param8.refParamType = ptype; } d.paramCount = d.paramCount + 1; @@ -916,6 +936,9 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bo 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) { diff --git a/_selfhost/src/scope.bux b/_selfhost/src/scope.bux index 2809f46..00cee9c 100644 --- a/_selfhost/src/scope.bux +++ b/_selfhost/src/scope.bux @@ -72,7 +72,7 @@ func Scope_Lookup(scope: *Scope, name: String) -> Symbol { } cur = cur.parent; } - var empty: Symbol; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } @@ -84,7 +84,7 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol { } i = i + 1; } - var empty: Symbol; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } diff --git a/_selfhost/src/sema.bux b/_selfhost/src/sema.bux index c63835f..ac07765 100644 --- a/_selfhost/src/sema.bux +++ b/_selfhost/src/sema.bux @@ -231,11 +231,10 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { // Call if kind == ekCall { discard Sema_CheckExpr(sema, expr.child1); - if expr.child2 != null as *Expr { - discard Sema_CheckExpr(sema, expr.child2); - } - if expr.child3 != null as *Expr { - discard Sema_CheckExpr(sema, expr.child3); + var arg: *ExprList = expr.callArgs; + while arg != null as *ExprList { + discard Sema_CheckExpr(sema, arg.expr); + arg = arg.next; } // Try to resolve return type from function declaration if expr.child1.kind == ekIdent { @@ -635,6 +634,7 @@ func Sema_Analyze(mod: *Module) -> *Sema { // Add type params to scope if decl.typeParamCount >= 1 { var tpSym: Symbol; + Sema_ZeroInitSymbol(&tpSym); tpSym.kind = skType; tpSym.name = decl.typeParam0; tpSym.typeKind = tyTypeParam; @@ -643,6 +643,7 @@ func Sema_Analyze(mod: *Module) -> *Sema { } if decl.typeParamCount >= 2 { var tpSym2: Symbol; + Sema_ZeroInitSymbol(&tpSym2); tpSym2.kind = skType; tpSym2.name = decl.typeParam1; tpSym2.typeKind = tyTypeParam; @@ -660,6 +661,9 @@ func Sema_Analyze(mod: *Module) -> *Sema { else if i == 3 { p = &decl.param3; } else if i == 4 { p = &decl.param4; } else if i == 5 { p = &decl.param5; } + else if i == 6 { p = &decl.param6; } + else if i == 7 { p = &decl.param7; } + else if i == 8 { p = &decl.param8; } var pSym: Symbol; Sema_ZeroInitSymbol(&pSym); pSym.kind = skVar; @@ -679,6 +683,9 @@ func Sema_Analyze(mod: *Module) -> *Sema { else if i == 3 { pSym.name = decl.param3.name; } else if i == 4 { pSym.name = decl.param4.name; } else if i == 5 { pSym.name = decl.param5.name; } + else if i == 6 { pSym.name = decl.param6.name; } + else if i == 7 { pSym.name = decl.param7.name; } + else if i == 8 { pSym.name = decl.param8.name; } discard Scope_Define(&funcScope, pSym); i = i + 1; } diff --git a/bux-lang-01.jpeg b/bux-lang-01.jpeg new file mode 100644 index 0000000..de974a4 Binary files /dev/null and b/bux-lang-01.jpeg differ diff --git a/docs/BuildAndTest.md b/docs/BuildAndTest.md index b1a0672..cd60bb8 100644 --- a/docs/BuildAndTest.md +++ b/docs/BuildAndTest.md @@ -32,7 +32,13 @@ make build make dev ``` -The output is a single binary: `buxc`. +The output is a single binary: `buxc` (bootstrap compiler in Nim). + +The self-hosted compiler `buxc2` is built from `src_bux/*.bux` sources via: +```bash +make selfhost +``` +This compiles `buxc2` using the bootstrap compiler. The self-hosted compiler generates C code and invokes `cc` to produce native binaries. --- @@ -164,12 +170,18 @@ bux/ ./buxc build -v ``` -### Inspecting Generated C +### Inspecting Generated C (bootstrap) ```bash ./buxc build cat build/main.c ``` +### Inspecting Generated C (self-hosted) +```bash +cd _selfhost && ../buxc build +cat build/main.c +``` + ### Common Errors | Error | Cause | Fix | diff --git a/src_bux/ast.bux b/src_bux/ast.bux index 60b7a03..c8369d7 100644 --- a/src_bux/ast.bux +++ b/src_bux/ast.bux @@ -96,6 +96,11 @@ const ekMatch: int = 22; const ekSpawn: int = 24; const ekAwait: int = 25; +struct ExprList { + expr: *Expr, + next: *ExprList, +} + struct Expr { kind: int, line: uint32, @@ -121,6 +126,9 @@ struct Expr { // Struct init fields structName: String, structFieldCount: int, + // Call arguments (linked list for multi-arg support) + callArgs: *ExprList, + callArgCount: int, } // --------------------------------------------------------------------------- @@ -241,6 +249,9 @@ struct Decl { param3: Param, param4: Param, param5: Param, + param6: Param, + param7: Param, + param8: Param, retType: *TypeExpr, // Body refBody: *Block, @@ -361,7 +372,8 @@ func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr { child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, refType: null as *TypeExpr, refBlock: null as *Block, genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0, - structName: "", structFieldCount: 0 }; + structName: "", structFieldCount: 0, + callArgs: null as *ExprList, callArgCount: 0 }; } func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr { diff --git a/src_bux/c_backend.bux b/src_bux/c_backend.bux index c576288..958d8b8 100644 --- a/src_bux/c_backend.bux +++ b/src_bux/c_backend.bux @@ -134,12 +134,29 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) { if kind == hCall { StringBuilder_Append(&cbe.sb, node.strValue); StringBuilder_Append(&cbe.sb, "("); + var needsComma: bool = false; if node.child1 != null as *HirNode { CBE_EmitExpr(cbe, node.child1); + needsComma = true; } if node.child2 != null as *HirNode { - StringBuilder_Append(&cbe.sb, ", "); + if needsComma { + StringBuilder_Append(&cbe.sb, ", "); + } CBE_EmitExpr(cbe, node.child2); + needsComma = true; + } + // Emit extra args from linked list + var ai: int = 0; + var curExtra: *HirArgList = node.extraData as *HirArgList; + while ai < node.extraCount { + if needsComma { + StringBuilder_Append(&cbe.sb, ", "); + } + CBE_EmitExpr(cbe, curExtra.node); + needsComma = true; + curExtra = curExtra.next; + ai = ai + 1; } StringBuilder_Append(&cbe.sb, ")"); return; @@ -414,6 +431,9 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) { if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; } if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; } if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; } + if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; } + if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; } + if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; } // Emit type if String_Eq(ptype, "String") || String_Eq(ptype, "str") { StringBuilder_Append(&cbe.sb, "String "); @@ -474,6 +494,9 @@ func CBE_FuncHasGeneric(f: *HirFunc) -> bool { if pi == 3 { ptype = f.param3.typeName; } if pi == 4 { ptype = f.param4.typeName; } if pi == 5 { ptype = f.param5.typeName; } + if pi == 6 { ptype = f.param6.typeName; } + if pi == 7 { ptype = f.param7.typeName; } + if pi == 8 { ptype = f.param8.typeName; } if CBE_IsGenericTypeName(ptype) { return true; } pi = pi + 1; } diff --git a/src_bux/cli.bux b/src_bux/cli.bux index 5b422f0..d06db7c 100644 --- a/src_bux/cli.bux +++ b/src_bux/cli.bux @@ -400,8 +400,13 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int { Print("DEBUG: MergeFileInto "); PrintLine(path); + if !FileExists(path) { + Print("Error: stdlib file not found: "); + PrintLine(path); + return 0; + } let source: String = ReadFile(path); - if String_Eq(source, "") { return 0; } + if source == null as String || String_Eq(source, "") { return 0; } let lex: *Lexer = Lexer_Tokenize(source); if Lexer_DiagCount(lex) > 0 { return 0; } let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); diff --git a/src_bux/hir.bux b/src_bux/hir.bux index 3b3667d..2f37e06 100644 --- a/src_bux/hir.bux +++ b/src_bux/hir.bux @@ -34,6 +34,15 @@ const hMatch: int = 28; const hSpawn: int = 29; const hAwait: int = 30; +// --------------------------------------------------------------------------- +// HirArgList — linked list for call arguments beyond 2 +// --------------------------------------------------------------------------- + +struct HirArgList { + node: *HirNode, + next: *HirArgList, +} + // --------------------------------------------------------------------------- // HirNode — unified struct with tagged union pattern // --------------------------------------------------------------------------- @@ -76,6 +85,9 @@ struct HirFunc { param3: HirParam; param4: HirParam; param5: HirParam; + param6: HirParam; + param7: HirParam; + param8: HirParam; retTypeKind: int; retTypeName: String; body: *HirNode; diff --git a/src_bux/hir_lower.bux b/src_bux/hir_lower.bux index 811403a..d783a37 100644 --- a/src_bux/hir_lower.bux +++ b/src_bux/hir_lower.bux @@ -180,11 +180,34 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { // Lower receiver as first argument let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1); n.child1 = recv; - // Lower remaining arguments - if expr.child2 != null as *Expr { - let extra: *HirNode = Lcx_LowerExpr(ctx, expr.child2); - // Chain extra args after receiver via child2/child3 - n.child2 = extra; + // Lower remaining arguments from linked list + var arg: *ExprList = expr.callArgs; + var argIdx: int = 0; + while arg != null as *ExprList { + let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); + if argIdx == 0 { + n.child2 = lowered; + } else if argIdx == 1 { + // Third argument — start linked list + let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + firstExtra.node = lowered; + firstExtra.next = null as *HirArgList; + n.extraData = firstExtra as *void; + n.extraCount = 1; + } else { + // Additional args — append to linked list + var cur: *HirArgList = n.extraData as *HirArgList; + while cur.next != null as *HirArgList { + cur = cur.next; + } + let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + newNode.node = lowered; + newNode.next = null as *HirArgList; + cur.next = newNode; + n.extraCount = n.extraCount + 1; + } + arg = arg.next; + argIdx = argIdx + 1; } return n; } @@ -192,12 +215,36 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { n.strValue = expr.child1.strValue; } - // Lower arguments - if expr.child2 != null as *Expr { - n.child1 = Lcx_LowerExpr(ctx, expr.child2); - } - if expr.child3 != null as *Expr { - n.child2 = Lcx_LowerExpr(ctx, expr.child3); + // Lower arguments from linked list + var arg: *ExprList = expr.callArgs; + var argIdx: int = 0; + while arg != null as *ExprList { + let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); + if argIdx == 0 { + n.child1 = lowered; + } else if argIdx == 1 { + n.child2 = lowered; + } else if argIdx == 2 { + // Third argument — start linked list + let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + firstExtra.node = lowered; + firstExtra.next = null as *HirArgList; + n.extraData = firstExtra as *void; + n.extraCount = 1; + } else { + // Additional args — append to linked list + var cur: *HirArgList = n.extraData as *HirArgList; + while cur.next != null as *HirArgList { + cur = cur.next; + } + let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; + newNode.node = lowered; + newNode.next = null as *HirArgList; + cur.next = newNode; + n.extraCount = n.extraCount + 1; + } + arg = arg.next; + argIdx = argIdx + 1; } return n; } @@ -359,6 +406,9 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { sym.name = stmt.strValue; sym.typeKind = alloca.typeKind; sym.typeName = alloca.typeName; + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; discard Scope_Define(ctx.scope, sym); // store the init value n.kind = hStore; @@ -495,6 +545,9 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { Lcx_LowerParam(&f.param3, &decl.param3); Lcx_LowerParam(&f.param4, &decl.param4); Lcx_LowerParam(&f.param5, &decl.param5); + Lcx_LowerParam(&f.param6, &decl.param6); + Lcx_LowerParam(&f.param7, &decl.param7); + Lcx_LowerParam(&f.param8, &decl.param8); if decl.retType != null as *TypeExpr { f.retTypeName = decl.retType.typeName; @@ -514,12 +567,18 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { else if pi == 3 { p = &decl.param3; } else if pi == 4 { p = &decl.param4; } else if pi == 5 { p = &decl.param5; } + else if pi == 6 { p = &decl.param6; } + else if pi == 7 { p = &decl.param7; } + else if pi == 8 { p = &decl.param8; } if p != null as *Param && p.refParamType != null as *TypeExpr { var sym: Symbol; sym.kind = skVar; sym.name = p.name; sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); sym.typeName = p.refParamType.typeName; + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; discard Scope_Define(ctx.scope, sym); } pi = pi + 1; diff --git a/src_bux/lexer.bux b/src_bux/lexer.bux index 7fe64b1..0c5f8e6 100644 --- a/src_bux/lexer.bux +++ b/src_bux/lexer.bux @@ -711,7 +711,7 @@ func Lexer_GetToken(lex: *Lexer, index: int) -> LexToken { if index >= 0 && index < lex.tokenCount { return lex.tokens[index]; } - var empty: LexToken; + var empty: LexToken = LexToken { kind: tkEndOfFile, text: "", line: 0, column: 0 }; return empty; } diff --git a/src_bux/parser.bux b/src_bux/parser.bux index e05e678..cedcb4e 100644 --- a/src_bux/parser.bux +++ b/src_bux/parser.bux @@ -214,6 +214,8 @@ func parserMakeExpr(kind: int, line: uint32, col: uint32) -> *Expr { e.genericTypeArgCount = 0; e.structName = ""; e.structFieldCount = 0; + e.callArgs = null as *ExprList; + e.callArgCount = 0; return e; } @@ -337,18 +339,27 @@ func parserParsePostfix(p: *Parser) -> *Expr { 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); - } + // Parse all arguments into linked list + var argCount: int = 0; + var firstArg: *ExprList = null as *ExprList; + var lastArg: *ExprList = null as *ExprList; + while !parserCheck(p, tkRParen) { + let argExpr: *Expr = parserParseExpr(p); + let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; + argNode.expr = argExpr; + argNode.next = null as *ExprList; + if firstArg == null as *ExprList { + firstArg = argNode; + lastArg = argNode; + } else { + lastArg.next = argNode; + lastArg = argNode; } + argCount = argCount + 1; + if !parserMatch(p, tkComma) { break; } } + e.callArgs = firstArg; + e.callArgCount = argCount; discard parserExpect(p, tkRParen, "expected ')'"); left = e; continue; @@ -842,7 +853,7 @@ func parserParseParamList(p: *Parser) -> *Decl { discard parserExpect(p, tkLParen, "expected '('"); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { - if d.paramCount >= 6 { 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); @@ -865,6 +876,15 @@ func parserParseParamList(p: *Parser) -> *Decl { } else if d.paramCount == 5 { d.param5.name = nameTok.text; d.param5.refParamType = ptype; + } else if d.paramCount == 6 { + d.param6.name = nameTok.text; + d.param6.refParamType = ptype; + } else if d.paramCount == 7 { + d.param7.name = nameTok.text; + d.param7.refParamType = ptype; + } else if d.paramCount == 8 { + d.param8.name = nameTok.text; + d.param8.refParamType = ptype; } d.paramCount = d.paramCount + 1; @@ -916,6 +936,9 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bo 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) { diff --git a/src_bux/scope.bux b/src_bux/scope.bux index 2809f46..00cee9c 100644 --- a/src_bux/scope.bux +++ b/src_bux/scope.bux @@ -72,7 +72,7 @@ func Scope_Lookup(scope: *Scope, name: String) -> Symbol { } cur = cur.parent; } - var empty: Symbol; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } @@ -84,7 +84,7 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol { } i = i + 1; } - var empty: Symbol; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } diff --git a/src_bux/sema.bux b/src_bux/sema.bux index c63835f..ac07765 100644 --- a/src_bux/sema.bux +++ b/src_bux/sema.bux @@ -231,11 +231,10 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { // Call if kind == ekCall { discard Sema_CheckExpr(sema, expr.child1); - if expr.child2 != null as *Expr { - discard Sema_CheckExpr(sema, expr.child2); - } - if expr.child3 != null as *Expr { - discard Sema_CheckExpr(sema, expr.child3); + var arg: *ExprList = expr.callArgs; + while arg != null as *ExprList { + discard Sema_CheckExpr(sema, arg.expr); + arg = arg.next; } // Try to resolve return type from function declaration if expr.child1.kind == ekIdent { @@ -635,6 +634,7 @@ func Sema_Analyze(mod: *Module) -> *Sema { // Add type params to scope if decl.typeParamCount >= 1 { var tpSym: Symbol; + Sema_ZeroInitSymbol(&tpSym); tpSym.kind = skType; tpSym.name = decl.typeParam0; tpSym.typeKind = tyTypeParam; @@ -643,6 +643,7 @@ func Sema_Analyze(mod: *Module) -> *Sema { } if decl.typeParamCount >= 2 { var tpSym2: Symbol; + Sema_ZeroInitSymbol(&tpSym2); tpSym2.kind = skType; tpSym2.name = decl.typeParam1; tpSym2.typeKind = tyTypeParam; @@ -660,6 +661,9 @@ func Sema_Analyze(mod: *Module) -> *Sema { else if i == 3 { p = &decl.param3; } else if i == 4 { p = &decl.param4; } else if i == 5 { p = &decl.param5; } + else if i == 6 { p = &decl.param6; } + else if i == 7 { p = &decl.param7; } + else if i == 8 { p = &decl.param8; } var pSym: Symbol; Sema_ZeroInitSymbol(&pSym); pSym.kind = skVar; @@ -679,6 +683,9 @@ func Sema_Analyze(mod: *Module) -> *Sema { else if i == 3 { pSym.name = decl.param3.name; } else if i == 4 { pSym.name = decl.param4.name; } else if i == 5 { pSym.name = decl.param5.name; } + else if i == 6 { pSym.name = decl.param6.name; } + else if i == 7 { pSym.name = decl.param7.name; } + else if i == 8 { pSym.name = decl.param8.name; } discard Scope_Define(&funcScope, pSym); i = i + 1; }