// cli.bux — CLI driver for the Bux self-hosting compiler // Wires together: Lexer → Parser → Sema → HirLower → CBackend 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_cc(c_file: String, out_bin: String, runtime_c: String, io_c: String, math_lib: String) -> int; extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String; 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 let lex: *Lexer = Lexer_Tokenize(source); 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 let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { PrintLine("Parse failed"); return ""; } // Phase 3: Semantic analysis 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 ""; } // Phase 4: 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: C code generation let cCode: String = CBackend_Generate(hirMod); // Cleanup Lexer_Free(lex); Sema_Free(sema); return cCode; } // --------------------------------------------------------------------------- // Build command // --------------------------------------------------------------------------- func Cli_Build(srcPath: String, outPath: String) -> int { let source: String = ReadFile(srcPath); if String_Eq(source, "") || !FileExists(srcPath) { Print("Error: cannot read source file: "); PrintLine(srcPath); return 1; } Print("Compiling "); PrintLine(srcPath); let cCode: String = Cli_Compile(source, srcPath); if String_Eq(cCode, "") { PrintLine("Compilation failed"); return 1; } // Write C output let ok: bool = WriteFile(outPath, cCode); if ok { Print(" → C code 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 { let source: String = ReadFile(srcPath); if String_Eq(source, "") { PrintLine("Error: cannot read source file"); return 1; } let cCode: String = Cli_Compile(source, srcPath); if String_Eq(cCode, "") { return 1; } 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 let lex: *Lexer = Lexer_Tokenize(source); 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; } func Cli_BuildProject(projectDir: String) -> int { let srcDir: String = bux_path_join(projectDir, "src"); 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 merged module let merged: *Module = bux_alloc(sizeof(Module)) as *Module; merged.name = "main"; merged.path = ""; merged.itemCount = 0; merged.firstItem = null as *Decl; // Parse each file and merge declarations into merged 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 merged 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 = merged.firstItem; merged.firstItem = inner; merged.itemCount = merged.itemCount + 1; inner = innerNext; } } else { decl.childDecl2 = merged.firstItem; merged.firstItem = decl; merged.itemCount = merged.itemCount + 1; } decl = next; } i = i + 1; } Print("Merged "); PrintInt(merged.itemCount); PrintLine(" declarations"); // Semantic analysis 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..."); let hirMod: *HirModule = HirLower_LowerModule(merged, sema); // C code generation PrintLine("Generating C code..."); let cCode: String = CBackend_Generate(hirMod); // Create build directory let buildDir: String = bux_path_join(projectDir, "build"); discard bux_mkdir_if_needed(buildDir); // Write C output let cFile: String = bux_path_join(buildDir, "main.c"); if !WriteFile(cFile, cCode) { PrintLine("Error: cannot write main.c"); return 1; } Print("C code written to "); PrintLine(cFile); // Find stdlib files let runtimeC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/runtime.c"); let ioC: String = bux_path_join(bux_path_join(projectDir, ".."), "stdlib/io.c"); // Invoke C compiler PrintLine("Invoking C compiler..."); let outBin: String = bux_path_join(buildDir, "buxc3"); let rc: int = bux_run_cc(cFile, outBin, runtimeC, ioC, "-lm"); if rc != 0 { PrintLine("Error: C compilation failed"); return 1; } Print("Build successful: "); PrintLine(outBin); return 0; } // --------------------------------------------------------------------------- // Main entry — dispatch based on args // --------------------------------------------------------------------------- func Cli_Run(args: *String, argCount: int) -> int { 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]; 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.c"; 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); } Print("Unknown command: "); PrintLine(cmd); return 1; } }