// cli.bux — CLI driver for the Bux self-hosting compiler // Wires together: Lexer → Parser → Sema → HirLower → NimBackend module Cli { extern func PrintLine(s: String); extern func Print(s: String); extern func bux_read_file(path: String) -> String; extern func bux_write_file(path: String, content: String) -> bool; extern func bux_file_exists(path: String) -> int; extern func bux_dir_exists(path: String) -> int; extern func bux_path_join(a: String, b: String) -> String; extern func bux_mkdir_if_needed(path: String) -> int; extern func bux_run_nim(nim_file: String, out_bin: String) -> int; extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String; extern func bux_system(cmd: String) -> int; func ReadFile(path: String) -> String { return bux_read_file(path); } func WriteFile(path: String, content: String) -> bool { return bux_write_file(path, content); } func FileExists(path: String) -> bool { return bux_file_exists(path) != 0; } func DirExists(path: String) -> bool { return bux_dir_exists(path) != 0; } // Import the compiler pipeline // In self-hosting mode, these are compiled together from src_bux/ // --------------------------------------------------------------------------- // Compile a single .bux source file // --------------------------------------------------------------------------- func Cli_Compile(source: String, sourceName: String) -> String { // Phase 1: Lex PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { PrintLine("Lex errors:"); var i: int = 0; while i < Lexer_DiagCount(lex) { Print(" "); PrintLine(lex.diags[i].message); i = i + 1; } return ""; } // Phase 2: Parse PrintLine(" Parsing..."); let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { PrintLine("Parse failed"); return ""; } PrintLine(" Parse done"); // Flatten module wrappers: find module decl and hoist its children var decl: *Decl = mod.firstItem; while decl != null as *Decl { if decl.kind == dkModule && decl.childDecl1 != null as *Decl { mod.firstItem = decl.childDecl1; break; } decl = decl.childDecl2; } // Phase 3: Semantic analysis PrintLine(" Sema..."); let sema: *Sema = Sema_Analyze(mod); if Sema_HasError(sema) { PrintLine("Sema errors:"); var i: int = 0; while i < Sema_DiagCount(sema) { Print(" "); PrintLine(sema.diags[i].message); i = i + 1; } return ""; } PrintLine(" Sema done"); // Phase 4: HIR lowering PrintLine(" HIR lowering..."); let hirMod: *HirModule = HirLower_LowerModule(mod, sema); if hirMod == null as *HirModule { PrintLine("HIR lowering failed"); return ""; } Print("HIR funcCount="); PrintInt(hirMod.funcCount); PrintLine(""); // Phase 5: QBE SSA code generation let qbeCode: String = QbeBackend_Generate(hirMod); // Cleanup Lexer_Free(lex); Sema_Free(sema); return qbeCode; } // --------------------------------------------------------------------------- // Build command // --------------------------------------------------------------------------- func Cli_Build(srcPath: String, outPath: String) -> int { let source: String = ReadFile(srcPath); if source == null as String || String_Eq(source, "") || !FileExists(srcPath) { Print("Error: cannot read source file: "); PrintLine(srcPath); return 1; } Print("Compiling "); PrintLine(srcPath); let ssaCode: String = Cli_Compile(source, srcPath); if String_Eq(ssaCode, "") { PrintLine("Compilation failed"); return 1; } // Write SSA output let ok: bool = WriteFile(outPath, ssaCode); if ok { Print(" → SSA written to "); PrintLine(outPath); return 0; } else { PrintLine("Error: cannot write output file"); return 1; } } // --------------------------------------------------------------------------- // Check command (compile only, no output) // --------------------------------------------------------------------------- func Cli_Check(srcPath: String) -> int { Print("Check: "); PrintLine(srcPath); let source: String = ReadFile(srcPath); if source == null as String || String_Eq(source, "") { PrintLine("Error: cannot read source file"); return 1; } PrintLine(" File read OK"); // Phase 1: Lex PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { PrintLine("Lex errors found"); return 1; } // Phase 2: Parse let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { PrintLine("Parse failed"); return 1; } // Flatten module wrappers var decl2: *Decl = mod.firstItem; while decl2 != null as *Decl { if decl2.kind == dkModule && decl2.childDecl1 != null as *Decl { mod.firstItem = decl2.childDecl1; break; } decl2 = decl2.childDecl2; } // Phase 3: Sema let sema: *Sema = Sema_Analyze(mod); if Sema_HasError(sema) { PrintLine("Sema errors found"); return 1; } // Phase 4: HIR lowering let hirMod: *HirModule = HirLower_LowerModule(mod, sema); var dc: *Decl = mod.firstItem; while dc != null as *Decl { if dc.kind == dkFunc { Print("check func "); PrintLine(dc.strValue); } dc = dc.childDecl2; } PrintLine("Check passed"); return 0; } // --------------------------------------------------------------------------- // Project build — compile all .bux files in src/ directory // --------------------------------------------------------------------------- func Cli_CompileSource(source: String, sourceName: String) -> *HirModule { // Phase 1: Lex PrintLine(" Lexing..."); let lex: *Lexer = Lexer_Tokenize(source); PrintLine(" Lex done"); if Lexer_DiagCount(lex) > 0 { Print("Lex errors in "); PrintLine(sourceName); return null as *HirModule; } // Phase 2: Parse let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { Print("Parse failed for "); PrintLine(sourceName); return null as *HirModule; } // Phase 3: Semantic analysis let sema: *Sema = Sema_Analyze(mod); if Sema_HasError(sema) { Print("Sema errors in "); PrintLine(sourceName); return null as *HirModule; } // Phase 4: HIR lowering let hirMod: *HirModule = HirLower_LowerModule(mod, sema); return hirMod; } // --------------------------------------------------------------------------- // Stdlib discovery and merging // --------------------------------------------------------------------------- func Cli_FindStdlibDir(projectDir: String) -> String { var path: String = bux_path_join(projectDir, "stdlib"); if DirExists(path) { return path; } path = bux_path_join(projectDir, "../stdlib"); if DirExists(path) { return path; } path = "/home/ziko/z-git/bux/bux/stdlib"; if DirExists(path) { return path; } return ""; } func Cli_DeclName(decl: *Decl) -> String { if decl == null as *Decl { return ""; } return decl.strValue; } func Cli_NameInList(name: String, names: *String, count: int) -> bool { if String_Eq(name, "") { return false; } var i: int = 0; while i < count { if String_Eq(names[i], name) { return true; } i = i + 1; } return false; } func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int { if mod == null as *Module { return 0; } var count: int = 0; var decl: *Decl = mod.firstItem; while decl != null as *Decl && count < maxCount { let next: *Decl = decl.childDecl2; if decl.kind == dkModule { var inner: *Decl = decl.childDecl1; while inner != null as *Decl && count < maxCount { let iname: String = Cli_DeclName(inner); if !String_Eq(iname, "") { names[count] = iname; count = count + 1; } inner = inner.childDecl2; } } else { let dname: String = Cli_DeclName(decl); if !String_Eq(dname, "") { names[count] = dname; count = count + 1; } } decl = next; } return count; } func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int { Print("DEBUG: MergeFileInto "); PrintLine(path); let source: String = ReadFile(path); if 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); if mod == null as *Module { return 0; } var added: int = 0; var decl: *Decl = mod.firstItem; while decl != null as *Decl { let next: *Decl = decl.childDecl2; decl.childDecl2 = null as *Decl; if decl.kind == dkModule { var inner: *Decl = decl.childDecl1; while inner != null as *Decl { let innerNext: *Decl = inner.childDecl2; let iname: String = Cli_DeclName(inner); if !String_Eq(iname, "") && Cli_NameInList(iname, skipNames, skipCount) { // Skip shadowed stdlib decl } else { inner.childDecl2 = target.firstItem; target.firstItem = inner; target.itemCount = target.itemCount + 1; added = added + 1; } inner = innerNext; } } else { let dname: String = Cli_DeclName(decl); if !String_Eq(dname, "") && Cli_NameInList(dname, skipNames, skipCount) { // Skip shadowed stdlib decl } else { decl.childDecl2 = target.firstItem; target.firstItem = decl; target.itemCount = target.itemCount + 1; added = added + 1; } } decl = next; } Print("DEBUG: MergeFileInto done, added="); PrintInt(added); PrintLine(""); return added; } func Cli_CopyModuleDecls(target: *Module, source: *Module) { if source == null as *Module || source.firstItem == null as *Decl { return; } Print("DEBUG: copy start, source.itemCount="); PrintInt(source.itemCount); PrintLine(""); var decl: *Decl = source.firstItem; var limit: int = 0; while decl != null as *Decl && limit < 10000 { let next: *Decl = decl.childDecl2; decl.childDecl2 = target.firstItem; target.firstItem = decl; target.itemCount = target.itemCount + 1; decl = next; limit = limit + 1; } source.firstItem = null as *Decl; source.itemCount = 0; PrintLine("DEBUG: copy end"); } func Cli_BuildProject(projectDir: String) -> int { PrintLine("DEBUG: Cli_BuildProject start"); let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); PrintLine("DEBUG: manifest loaded"); var outName: String = man.name; if String_Eq(outName, "") { outName = "bux_out"; } let srcDir: String = bux_path_join(projectDir, "src"); PrintLine("DEBUG: srcDir built"); if !DirExists(srcDir) { Print("Error: no src/ directory in "); PrintLine(projectDir); return 1; } Print("Scanning "); PrintLine(srcDir); // List all .bux files in src/ var fileCount: int = 0; let files: *String = bux_list_dir(srcDir, ".bux", &fileCount); if fileCount == 0 { PrintLine("Error: no .bux files found in src/"); return 1; } Print("Found "); PrintInt(fileCount); PrintLine(" source files"); // Create user merged module (collect user decls first) let userMerged: *Module = bux_alloc(sizeof(Module)) as *Module; userMerged.name = "main"; userMerged.path = ""; userMerged.itemCount = 0; userMerged.firstItem = null as *Decl; // Parse each file and merge declarations into userMerged module var i: int = 0; while i < fileCount { let path: String = files[i]; Print(" Parsing "); PrintLine(path); let source: String = ReadFile(path); if String_Eq(source, "") { Print(" Error: cannot read "); PrintLine(path); return 1; } let lex: *Lexer = Lexer_Tokenize(source); if Lexer_DiagCount(lex) > 0 { Print(" Lex errors in "); PrintLine(path); return 1; } let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { Print(" Parse failed for "); PrintLine(path); return 1; } // Merge declarations from this module into userMerged var decl: *Decl = mod.firstItem; while decl != null as *Decl { let next: *Decl = decl.childDecl2; decl.childDecl2 = null as *Decl; // Flatten module declarations if decl.kind == dkModule { var inner: *Decl = decl.childDecl1; while inner != null as *Decl { let innerNext: *Decl = inner.childDecl2; inner.childDecl2 = userMerged.firstItem; userMerged.firstItem = inner; userMerged.itemCount = userMerged.itemCount + 1; inner = innerNext; } } else { decl.childDecl2 = userMerged.firstItem; userMerged.firstItem = decl; userMerged.itemCount = userMerged.itemCount + 1; } decl = next; } i = i + 1; } Print("DEBUG: userMerged.itemCount="); PrintInt(userMerged.itemCount); PrintLine(""); // Collect user declaration names for shadow detection let maxNames: int = 2048; let userNames: *String = bux_alloc(maxNames * 8) as *String; let userNameCount: int = Cli_CollectNames(userMerged, userNames, maxNames); // Create final merged module let merged: *Module = bux_alloc(sizeof(Module)) as *Module; merged.name = "main"; merged.path = ""; merged.itemCount = 0; merged.firstItem = null as *Decl; // Find and merge stdlib declarations let stdlibDir: String = Cli_FindStdlibDir(projectDir); if !String_Eq(stdlibDir, "") { Print("Stdlib found: "); PrintLine(stdlibDir); let stdSubdir: String = bux_path_join(stdlibDir, "Std"); if DirExists(stdSubdir) { var stdFileCount: int = 0; let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount); Print("DEBUG: stdFileCount="); PrintInt(stdFileCount); PrintLine(""); if stdFileCount > 0 { Print("Merging "); PrintInt(stdFileCount); PrintLine(" stdlib files"); var si: int = 0; var stdAdded: int = 0; while si < stdFileCount { Print("DEBUG: merging stdlib file "); PrintInt(si); PrintLine(""); // Skip Channel.bux for debugging if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") { PrintLine("DEBUG: skipping Channel.bux"); si = si + 1; continue; } let added: int = Cli_MergeFileInto(merged, stdFiles[si], userNames, userNameCount); stdAdded = stdAdded + added; si = si + 1; } Print("Stdlib declarations added: "); PrintInt(stdAdded); PrintLine(""); } } } // Copy user declarations into merged (user shadows stdlib) PrintLine("DEBUG: before copy"); var countTest: int = 0; var dtest: *Decl = userMerged.firstItem; while dtest != null as *Decl && countTest < 10000 { dtest = dtest.childDecl2; countTest = countTest + 1; } Print("userMerged actual count="); PrintInt(countTest); PrintLine(""); Print("userMerged.itemCount="); PrintInt(userMerged.itemCount); PrintLine(""); var du: *Decl = userMerged.firstItem; while du != null as *Decl { if du.kind == dkFunc { Print("user func "); PrintLine(du.strValue); } du = du.childDecl2; } Cli_CopyModuleDecls(merged, userMerged); PrintLine("DEBUG: copy done"); Print("merged.itemCount="); PrintInt(merged.itemCount); PrintLine(""); PrintLine("DEBUG: copy done"); Print("Merged "); PrintInt(merged.itemCount); PrintLine(" declarations"); // Semantic analysis PrintLine("DEBUG: about to run sema"); PrintLine("Running sema..."); let sema: *Sema = Sema_Analyze(merged); if Sema_HasError(sema) { PrintLine("Sema errors found"); return 1; } // HIR lowering PrintLine("Lowering to HIR..."); var di: *Decl = merged.firstItem; while di != null as *Decl { if di.kind == dkFunc { Print("decl func "); PrintLine(di.strValue); } di = di.childDecl2; } let hirMod: *HirModule = HirLower_LowerModule(merged, sema); // QBE SSA code generation PrintLine("Generating QBE SSA..."); var fi: int = 0; while fi < hirMod.funcCount { Print("func "); PrintLine(hirMod.funcs[fi].name); fi = fi + 1; } let qbeCode: String = QbeBackend_Generate(hirMod); // Create build directory let buildDir: String = bux_path_join(projectDir, "build"); discard bux_mkdir_if_needed(buildDir); // Write SSA output let ssaFile: String = bux_path_join(buildDir, "main.ssa"); if !WriteFile(ssaFile, qbeCode) { PrintLine("Error: cannot write main.ssa"); return 1; } Print("SSA written to "); PrintLine(ssaFile); // Invoke QBE to generate assembly PrintLine("Invoking QBE..."); let asmFile: String = bux_path_join(buildDir, "main.s"); var qbeCmd: String = bux_path_join(projectDir, "vendor/qbe/qbe"); // Try relative to project first, then absolute if !FileExists(qbeCmd) { // Try from compiler location qbeCmd = "../vendor/qbe/qbe"; } if !FileExists(qbeCmd) { qbeCmd = "vendor/qbe/qbe"; } // Actually let's use bux_system with a constructed command var cmdBuf: StringBuilder = StringBuilder_NewCap(256); StringBuilder_Append(&cmdBuf, qbeCmd); StringBuilder_Append(&cmdBuf, " -o "); StringBuilder_Append(&cmdBuf, asmFile); StringBuilder_Append(&cmdBuf, " "); StringBuilder_Append(&cmdBuf, ssaFile); let qbeRc: int = bux_system(StringBuilder_Build(&cmdBuf)); if qbeRc != 0 { PrintLine("Error: QBE compilation failed"); return 1; } // Link with cc PrintLine("Linking..."); var linkBuf: StringBuilder = StringBuilder_NewCap(512); StringBuilder_Append(&linkBuf, "cc -o "); let outBin: String = bux_path_join(buildDir, outName); StringBuilder_Append(&linkBuf, outBin); StringBuilder_Append(&linkBuf, " "); StringBuilder_Append(&linkBuf, asmFile); StringBuilder_Append(&linkBuf, " "); // Find runtime.c and io.c var rtPath: String = bux_path_join(projectDir, "stdlib/runtime.c"); var ioPath: String = bux_path_join(projectDir, "stdlib/io.c"); if !FileExists(rtPath) { rtPath = bux_path_join(projectDir, "../stdlib/runtime.c"); } if !FileExists(ioPath) { ioPath = bux_path_join(projectDir, "../stdlib/io.c"); } if FileExists(rtPath) { StringBuilder_Append(&linkBuf, rtPath); StringBuilder_Append(&linkBuf, " "); } if FileExists(ioPath) { StringBuilder_Append(&linkBuf, ioPath); StringBuilder_Append(&linkBuf, " "); } StringBuilder_Append(&linkBuf, "-lm"); let linkRc: int = bux_system(StringBuilder_Build(&linkBuf)); if linkRc != 0 { PrintLine("Error: Linking failed"); return 1; } Print("Build successful: "); PrintLine(outBin); return 0; } // --------------------------------------------------------------------------- // Run command — build project and execute // --------------------------------------------------------------------------- func Cli_RunProject(projectDir: String) -> int { PrintLine("DEBUG: Cli_RunProject start"); let rc: int = Cli_BuildProject(projectDir); if rc != 0 { return rc; } let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); var outName: String = man.name; if String_Eq(outName, "") { outName = "bux_out"; } let outBin: String = bux_path_join(bux_path_join(projectDir, "build"), outName); Print("Running: "); PrintLine(outBin); return bux_system(outBin); } // --------------------------------------------------------------------------- // Main entry — dispatch based on args // --------------------------------------------------------------------------- func Cli_Run(args: *String, argCount: int) -> int { Print("Cli_Run argCount="); PrintInt(argCount); PrintLine(""); if argCount < 2 { PrintLine("Bux Self-Hosting Compiler v0.2.0"); PrintLine("Usage: buxc [args]"); PrintLine("Commands: build, check, run, version"); PrintLine(""); PrintLine("Pipeline modules:"); PrintLine(" Lexer ✅ 695 lines"); PrintLine(" Parser ✅ 1004 lines"); PrintLine(" Sema ✅ 393 lines"); PrintLine(" HirLower ✅ 307 lines"); PrintLine(" CBackend ✅ 264 lines"); PrintLine(" Total: 3830 lines of Bux"); return 0; } let cmd: String = args[1]; Print("cmd="); PrintLine(cmd); if String_Eq(cmd, "version") { PrintLine("Bux 0.2.0 (self-hosting bootstrap)"); return 0; } if String_Eq(cmd, "check") { if argCount < 3 { PrintLine("Usage: buxc check "); return 1; } return Cli_Check(args[2]); } if String_Eq(cmd, "build") { let src: String = "src/Main.bux"; let out: String = "build/main.nim"; if argCount >= 3 { src = args[2]; } if argCount >= 4 { out = args[3]; } return Cli_Build(src, out); } if String_Eq(cmd, "project") { let dir: String = "."; if argCount >= 3 { dir = args[2]; } return Cli_BuildProject(dir); } if String_Eq(cmd, "run") { PrintLine("DEBUG: run command"); let dir: String = "."; if argCount >= 3 { dir = args[2]; } return Cli_RunProject(dir); } Print("Unknown command: "); PrintLine(cmd); return 1; } }