selfhost: C backend becomes primary backend; QBE path removed from CLI
Major changes:
- Cli_BuildProject now uses CBackend_Generate instead of QBE SSA generation
- Replaced QBE invocation (vendor/qbe/qbe) with direct cc compilation
- Cli_Build (single-file) now compiles C output with cc including runtime.c/io.c
- Added import-based stdlib merging: only imported Std::* modules are merged,
eliminating unused generic code from Array/Map/Set/Channel in simple programs
C backend fixes:
- Fixed struct typedef redefinition: struct definitions now use 'struct Name {}'
instead of 'typedef struct {} Name' to avoid conflicting with forward typedefs
- Added enum support: HirEnum struct, enum emission in C backend, variant
constants emitted as #define Name_Value
- Added char8 typedef
- Skip generic structs/functions (T/K/V type parameters) in C emission
Sema fixes:
- Fixed Sema_IsNumeric: now correctly recognizes uint, float32, float64
- Fixed return type check: allow pointer compatibility (String <-> *T)
Tested: hello, fibonacci, factorial, enums examples build and run via buxc2
This commit is contained in:
@@ -440,6 +440,47 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
|||||||
StringBuilder_Append(&cbe.sb, ")");
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helpers for detecting generic declarations (not yet monomorphized)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBE_IsGenericTypeName(name: String) -> bool {
|
||||||
|
if String_Eq(name, "T") || String_Eq(name, "K") || String_Eq(name, "V") { return true; }
|
||||||
|
if String_Eq(name, "T*") || String_Eq(name, "K*") || String_Eq(name, "V*") { return true; }
|
||||||
|
// Generic container structs and their pointer variants
|
||||||
|
if String_Eq(name, "Array") || String_Eq(name, "Array*") { return true; }
|
||||||
|
if String_Eq(name, "SetEntry") || String_Eq(name, "SetEntry*") { return true; }
|
||||||
|
if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; }
|
||||||
|
if String_Eq(name, "MapEntry") || String_Eq(name, "MapEntry*") { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_StructHasGeneric(st: *HirStruct) -> bool {
|
||||||
|
var fi: int = 0;
|
||||||
|
while fi < st.fieldCount {
|
||||||
|
if CBE_IsGenericTypeName(st.fields[fi].typeName) { return true; }
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
|
||||||
|
var pi: int = 0;
|
||||||
|
while pi < f.paramCount {
|
||||||
|
var ptype: String = "";
|
||||||
|
if pi == 0 { ptype = f.param0.typeName; }
|
||||||
|
if pi == 1 { ptype = f.param1.typeName; }
|
||||||
|
if pi == 2 { ptype = f.param2.typeName; }
|
||||||
|
if pi == 3 { ptype = f.param3.typeName; }
|
||||||
|
if pi == 4 { ptype = f.param4.typeName; }
|
||||||
|
if pi == 5 { ptype = f.param5.typeName; }
|
||||||
|
if CBE_IsGenericTypeName(ptype) { return true; }
|
||||||
|
pi = pi + 1;
|
||||||
|
}
|
||||||
|
if CBE_IsGenericTypeName(f.retTypeName) { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Generate complete C module
|
// Generate complete C module
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -466,27 +507,62 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
|
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
|
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
|
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef double float64;\n\n");
|
StringBuilder_Append(&cbe.sb, "typedef double float64;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef char char8;\n\n");
|
||||||
// Runtime declarations
|
// Runtime declarations
|
||||||
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
|
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
|
||||||
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
|
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
|
||||||
|
|
||||||
// Forward declare all struct types
|
// Forward declare all struct types (skip generics)
|
||||||
var si: int = 0;
|
var si: int = 0;
|
||||||
while si < mod.structCount {
|
while si < mod.structCount {
|
||||||
|
if !CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
}
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Struct definitions
|
// Enum definitions (typedef to int)
|
||||||
|
var ei: int = 0;
|
||||||
|
while ei < mod.enumCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef int ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.enums[ei].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
ei = ei + 1;
|
||||||
|
}
|
||||||
|
if mod.enumCount > 0 {
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant definitions
|
||||||
|
var ci: int = 0;
|
||||||
|
while ci < mod.constCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "#define ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.consts[ci].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, String_FromInt(mod.consts[ci].value as int64));
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
if mod.constCount > 0 {
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct definitions (skip generics)
|
||||||
si = 0;
|
si = 0;
|
||||||
while si < mod.structCount {
|
while si < mod.structCount {
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct {\n");
|
if CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "struct ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
var fi: int = 0;
|
var fi: int = 0;
|
||||||
while fi < mod.structs[si].fieldCount {
|
while fi < mod.structs[si].fieldCount {
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
@@ -508,17 +584,17 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "} ");
|
StringBuilder_Append(&cbe.sb, "};\n\n");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
|
||||||
StringBuilder_Append(&cbe.sb, ";\n\n");
|
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward declarations for all functions
|
// Forward declarations for all functions (skip generics)
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
|
if !CBE_FuncHasGeneric(&mod.funcs[i]) {
|
||||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
}
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
@@ -532,11 +608,16 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Function definitions
|
// Function definitions (skip generics)
|
||||||
var hasMain: bool = false;
|
var hasMain: bool = false;
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||||
|
// Skip generic functions
|
||||||
|
if CBE_FuncHasGeneric(&mod.funcs[i]) {
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Skip forward declarations (functions without body)
|
// Skip forward declarations (functions without body)
|
||||||
let body: *HirNode = mod.funcs[i].body;
|
let body: *HirNode = mod.funcs[i].body;
|
||||||
if body == null as *HirNode {
|
if body == null as *HirNode {
|
||||||
|
|||||||
+160
-96
@@ -1,5 +1,5 @@
|
|||||||
// cli.bux — CLI driver for the Bux self-hosting compiler
|
// cli.bux — CLI driver for the Bux self-hosting compiler
|
||||||
// Wires together: Lexer → Parser → Sema → HirLower → NimBackend
|
// Wires together: Lexer → Parser → Sema → HirLower → CBackend
|
||||||
module Cli {
|
module Cli {
|
||||||
|
|
||||||
extern func PrintLine(s: String);
|
extern func PrintLine(s: String);
|
||||||
@@ -97,14 +97,14 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
|||||||
Print("HIR funcCount=");
|
Print("HIR funcCount=");
|
||||||
PrintInt(hirMod.funcCount);
|
PrintInt(hirMod.funcCount);
|
||||||
|
|
||||||
// Phase 5: QBE SSA code generation
|
// Phase 5: C code generation
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
let cCode: String = CBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Lexer_Free(lex);
|
Lexer_Free(lex);
|
||||||
Sema_Free(sema);
|
Sema_Free(sema);
|
||||||
|
|
||||||
return qbeCode;
|
return cCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -122,23 +122,64 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
Print("Compiling ");
|
Print("Compiling ");
|
||||||
PrintLine(srcPath);
|
PrintLine(srcPath);
|
||||||
|
|
||||||
let ssaCode: String = Cli_Compile(source, srcPath);
|
let cCode: String = Cli_Compile(source, srcPath);
|
||||||
|
|
||||||
if String_Eq(ssaCode, "") {
|
if String_Eq(cCode, "") {
|
||||||
PrintLine("Compilation failed");
|
PrintLine("Compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write SSA output
|
// Write C output
|
||||||
let ok: bool = WriteFile(outPath, ssaCode);
|
let cFile: String = String_Concat(outPath, ".c");
|
||||||
if ok {
|
let ok: bool = WriteFile(cFile, cCode);
|
||||||
Print(" → SSA written to ");
|
if !ok {
|
||||||
PrintLine(outPath);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
PrintLine("Error: cannot write output file");
|
PrintLine("Error: cannot write output file");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Print(" → C written to ");
|
||||||
|
PrintLine(cFile);
|
||||||
|
|
||||||
|
// Find runtime.c and io.c for linking
|
||||||
|
var rtPath: String = "stdlib/runtime.c";
|
||||||
|
var ioPath: String = "stdlib/io.c";
|
||||||
|
if !FileExists(rtPath) {
|
||||||
|
rtPath = "../stdlib/runtime.c";
|
||||||
|
}
|
||||||
|
if !FileExists(ioPath) {
|
||||||
|
ioPath = "../stdlib/io.c";
|
||||||
|
}
|
||||||
|
if !FileExists(rtPath) {
|
||||||
|
rtPath = "/home/ziko/z-git/bux/bux/stdlib/runtime.c";
|
||||||
|
}
|
||||||
|
if !FileExists(ioPath) {
|
||||||
|
ioPath = "/home/ziko/z-git/bux/bux/stdlib/io.c";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile with cc
|
||||||
|
PrintLine("Compiling C...");
|
||||||
|
var cmdBuf: StringBuilder = StringBuilder_NewCap(512);
|
||||||
|
StringBuilder_Append(&cmdBuf, "cc -O2 -pthread -o ");
|
||||||
|
StringBuilder_Append(&cmdBuf, outPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
StringBuilder_Append(&cmdBuf, cFile);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
if FileExists(rtPath) {
|
||||||
|
StringBuilder_Append(&cmdBuf, rtPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
}
|
||||||
|
if FileExists(ioPath) {
|
||||||
|
StringBuilder_Append(&cmdBuf, ioPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cmdBuf, "-lm");
|
||||||
|
let ccRc: int = bux_system(StringBuilder_Build(&cmdBuf));
|
||||||
|
if ccRc != 0 {
|
||||||
|
PrintLine("Error: C compilation failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Print(" → Binary: ");
|
||||||
|
PrintLine(outPath);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -307,6 +348,55 @@ func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: convert single char int to String
|
||||||
|
func String_FromChar(c: int) -> String {
|
||||||
|
var buf: *char8 = bux_alloc(2) as *char8;
|
||||||
|
buf[0] = c as char8;
|
||||||
|
buf[1] = 0 as char8;
|
||||||
|
return buf as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect stdlib module paths imported by user code.
|
||||||
|
// Returns number of unique stdlib files to merge (paths written into outPaths).
|
||||||
|
func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, stdlibDir: String) -> int {
|
||||||
|
if mod == null as *Module { return 0; }
|
||||||
|
var count: int = 0;
|
||||||
|
var decl: *Decl = mod.firstItem;
|
||||||
|
while decl != null as *Decl && count < maxCount {
|
||||||
|
if decl.kind == dkUse {
|
||||||
|
let path: String = decl.usePath;
|
||||||
|
// Only handle Std::* imports
|
||||||
|
if String_StartsWith(path, "Std::") {
|
||||||
|
// Extract module name after Std:: (e.g., "Std::Io" -> "Io")
|
||||||
|
var j: int = 5; // skip "Std::"
|
||||||
|
var modName: String = "";
|
||||||
|
while j < 100 {
|
||||||
|
let c: int = path[j] as int;
|
||||||
|
if c == 0 || c == 58 { break; } // null or ':'
|
||||||
|
modName = String_Concat(modName, String_FromChar(c));
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if !String_Eq(modName, "") {
|
||||||
|
let filePath: String = bux_path_join(bux_path_join(stdlibDir, "Std"), String_Concat(modName, ".bux"));
|
||||||
|
// Check for duplicates
|
||||||
|
var dup: bool = false;
|
||||||
|
var di: int = 0;
|
||||||
|
while di < count {
|
||||||
|
if String_Eq(outPaths[di], filePath) { dup = true; }
|
||||||
|
di = di + 1;
|
||||||
|
}
|
||||||
|
if !dup {
|
||||||
|
outPaths[count] = filePath;
|
||||||
|
count = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decl = decl.childDecl2;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
|
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
|
||||||
Print("DEBUG: MergeFileInto ");
|
Print("DEBUG: MergeFileInto ");
|
||||||
PrintLine(path);
|
PrintLine(path);
|
||||||
@@ -473,33 +563,25 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
merged.itemCount = 0;
|
merged.itemCount = 0;
|
||||||
merged.firstItem = null as *Decl;
|
merged.firstItem = null as *Decl;
|
||||||
|
|
||||||
// Find and merge stdlib declarations
|
// Find and merge stdlib declarations (only imported modules)
|
||||||
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
||||||
if !String_Eq(stdlibDir, "") {
|
if !String_Eq(stdlibDir, "") {
|
||||||
Print("Stdlib found: ");
|
Print("Stdlib found: ");
|
||||||
PrintLine(stdlibDir);
|
PrintLine(stdlibDir);
|
||||||
let stdSubdir: String = bux_path_join(stdlibDir, "Std");
|
// Collect imported stdlib modules from user code
|
||||||
if DirExists(stdSubdir) {
|
let maxImports: int = 64;
|
||||||
var stdFileCount: int = 0;
|
let importPaths: *String = bux_alloc(maxImports * 8) as *String;
|
||||||
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
let importCount: int = Cli_CollectStdlibImports(userMerged, importPaths, maxImports, stdlibDir);
|
||||||
Print("DEBUG: stdFileCount=");
|
Print("Imported stdlib modules: ");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(importCount);
|
||||||
if stdFileCount > 0 {
|
PrintLine("");
|
||||||
Print("Merging ");
|
if importCount > 0 {
|
||||||
PrintInt(stdFileCount);
|
|
||||||
PrintLine(" stdlib files");
|
|
||||||
var si: int = 0;
|
var si: int = 0;
|
||||||
var stdAdded: int = 0;
|
var stdAdded: int = 0;
|
||||||
while si < stdFileCount {
|
while si < importCount {
|
||||||
Print("DEBUG: merging stdlib file ");
|
Print(" Merging ");
|
||||||
PrintInt(si);
|
PrintLine(importPaths[si]);
|
||||||
// Skip Channel.bux for debugging
|
let added: int = Cli_MergeFileInto(merged, importPaths[si], userNames, userNameCount);
|
||||||
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;
|
stdAdded = stdAdded + added;
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
@@ -507,7 +589,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
PrintInt(stdAdded);
|
PrintInt(stdAdded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Copy user declarations into merged (user shadows stdlib)
|
// Copy user declarations into merged (user shadows stdlib)
|
||||||
PrintLine("DEBUG: before copy");
|
PrintLine("DEBUG: before copy");
|
||||||
@@ -554,6 +635,14 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
let sema: *Sema = Sema_Analyze(merged);
|
let sema: *Sema = Sema_Analyze(merged);
|
||||||
if Sema_HasError(sema) {
|
if Sema_HasError(sema) {
|
||||||
PrintLine("Sema errors found");
|
PrintLine("Sema errors found");
|
||||||
|
var i: int = 0;
|
||||||
|
while i < Sema_DiagCount(sema) {
|
||||||
|
Print(" line ");
|
||||||
|
PrintInt(sema.diags[i].line as int64);
|
||||||
|
Print(": ");
|
||||||
|
PrintLine(sema.diags[i].message);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,85 +658,60 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
||||||
|
|
||||||
// QBE SSA code generation
|
// C code generation
|
||||||
PrintLine("Generating QBE SSA...");
|
PrintLine("Generating C code...");
|
||||||
var fi: int = 0;
|
let cCode: String = CBackend_Generate(hirMod);
|
||||||
while fi < hirMod.funcCount {
|
|
||||||
Print("func ");
|
|
||||||
PrintLine(hirMod.funcs[fi].name);
|
|
||||||
fi = fi + 1;
|
|
||||||
}
|
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
|
||||||
|
|
||||||
// Create build directory
|
// Create build directory
|
||||||
let buildDir: String = bux_path_join(projectDir, "build");
|
let buildDir: String = bux_path_join(projectDir, "build");
|
||||||
discard bux_mkdir_if_needed(buildDir);
|
discard bux_mkdir_if_needed(buildDir);
|
||||||
|
|
||||||
// Write SSA output
|
// Write C output
|
||||||
let ssaFile: String = bux_path_join(buildDir, "main.ssa");
|
let cFile: String = bux_path_join(buildDir, "main.c");
|
||||||
if !WriteFile(ssaFile, qbeCode) {
|
if !WriteFile(cFile, cCode) {
|
||||||
PrintLine("Error: cannot write main.ssa");
|
PrintLine("Error: cannot write main.c");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Print("C code written to ");
|
||||||
|
PrintLine(cFile);
|
||||||
|
|
||||||
Print("SSA written to ");
|
// Find runtime.c and io.c (use stdlibDir if available)
|
||||||
PrintLine(ssaFile);
|
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
||||||
|
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
||||||
// Invoke QBE to generate assembly
|
if !FileExists(rtPath) {
|
||||||
PrintLine("Invoking QBE...");
|
rtPath = bux_path_join(projectDir, "stdlib/runtime.c");
|
||||||
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) {
|
if !FileExists(ioPath) {
|
||||||
qbeCmd = "vendor/qbe/qbe";
|
ioPath = bux_path_join(projectDir, "stdlib/io.c");
|
||||||
}
|
}
|
||||||
// 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) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compile with cc
|
||||||
|
PrintLine("Compiling C...");
|
||||||
|
let outBin: String = bux_path_join(buildDir, outName);
|
||||||
|
var ccBuf: StringBuilder = StringBuilder_NewCap(512);
|
||||||
|
StringBuilder_Append(&ccBuf, "cc -O2 -pthread -o ");
|
||||||
|
StringBuilder_Append(&ccBuf, outBin);
|
||||||
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
|
StringBuilder_Append(&ccBuf, cFile);
|
||||||
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
if FileExists(rtPath) {
|
if FileExists(rtPath) {
|
||||||
StringBuilder_Append(&linkBuf, rtPath);
|
StringBuilder_Append(&ccBuf, rtPath);
|
||||||
StringBuilder_Append(&linkBuf, " ");
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
}
|
}
|
||||||
if FileExists(ioPath) {
|
if FileExists(ioPath) {
|
||||||
StringBuilder_Append(&linkBuf, ioPath);
|
StringBuilder_Append(&ccBuf, ioPath);
|
||||||
StringBuilder_Append(&linkBuf, " ");
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&linkBuf, "-lm");
|
StringBuilder_Append(&ccBuf, "-lm");
|
||||||
let linkRc: int = bux_system(StringBuilder_Build(&linkBuf));
|
let ccRc: int = bux_system(StringBuilder_Build(&ccBuf));
|
||||||
if linkRc != 0 {
|
if ccRc != 0 {
|
||||||
PrintLine("Error: Linking failed");
|
PrintLine("Error: C compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +757,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
PrintLine(" Parser ✅ 1004 lines");
|
PrintLine(" Parser ✅ 1004 lines");
|
||||||
PrintLine(" Sema ✅ 393 lines");
|
PrintLine(" Sema ✅ 393 lines");
|
||||||
PrintLine(" HirLower ✅ 307 lines");
|
PrintLine(" HirLower ✅ 307 lines");
|
||||||
PrintLine(" CBackend ✅ 264 lines");
|
PrintLine(" CBackend ✅ 585 lines");
|
||||||
PrintLine(" Total: 3830 lines of Bux");
|
PrintLine(" Total: 3830 lines of Bux");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -716,7 +780,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
|
|
||||||
if String_Eq(cmd, "build") {
|
if String_Eq(cmd, "build") {
|
||||||
let src: String = "src/Main.bux";
|
let src: String = "src/Main.bux";
|
||||||
let out: String = "build/main.nim";
|
let out: String = "build/main";
|
||||||
if argCount >= 3 { src = args[2]; }
|
if argCount >= 3 { src = args[2]; }
|
||||||
if argCount >= 4 { out = args[3]; }
|
if argCount >= 4 { out = args[3]; }
|
||||||
return Cli_Build(src, out);
|
return Cli_Build(src, out);
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ struct HirConst {
|
|||||||
value: int;
|
value: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct HirEnum {
|
||||||
|
name: String;
|
||||||
|
}
|
||||||
|
|
||||||
struct HirModule {
|
struct HirModule {
|
||||||
funcCount: int;
|
funcCount: int;
|
||||||
funcs: *HirFunc;
|
funcs: *HirFunc;
|
||||||
@@ -123,6 +127,7 @@ struct HirModule {
|
|||||||
structCount: int;
|
structCount: int;
|
||||||
structs: *HirStruct;
|
structs: *HirStruct;
|
||||||
enumCount: int;
|
enumCount: int;
|
||||||
|
enums: *HirEnum;
|
||||||
constCount: int;
|
constCount: int;
|
||||||
consts: *HirConst;
|
consts: *HirConst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,16 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
|
|
||||||
// Field access
|
// Field access
|
||||||
if kind == ekField {
|
if kind == ekField {
|
||||||
|
// Check if this is enum variant access: Color::Green
|
||||||
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
|
||||||
|
if sym.decl != null as *Decl && sym.decl.kind == dkEnum {
|
||||||
|
// Emit as variable reference: Color_Green
|
||||||
|
n.kind = hVar;
|
||||||
|
n.strValue = String_Concat(String_Concat(expr.child1.strValue, "_"), expr.strValue);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
n.kind = hFieldPtr;
|
n.kind = hFieldPtr;
|
||||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
n.strValue = expr.strValue;
|
n.strValue = expr.strValue;
|
||||||
@@ -543,6 +553,8 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
hm.funcs = ctx.funcs;
|
hm.funcs = ctx.funcs;
|
||||||
hm.structCount = 0;
|
hm.structCount = 0;
|
||||||
hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
|
hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
|
||||||
|
hm.enumCount = 0;
|
||||||
|
hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
|
||||||
hm.constCount = 0;
|
hm.constCount = 0;
|
||||||
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
||||||
|
|
||||||
@@ -628,7 +640,25 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
hm.constCount = hm.constCount + 1;
|
hm.constCount = hm.constCount + 1;
|
||||||
}
|
}
|
||||||
if decl.kind == dkEnum {
|
if decl.kind == dkEnum {
|
||||||
|
let ei: int = hm.enumCount;
|
||||||
|
hm.enums[ei].name = decl.strValue;
|
||||||
hm.enumCount = hm.enumCount + 1;
|
hm.enumCount = hm.enumCount + 1;
|
||||||
|
// Emit enum variants as constants: EnumName_VariantName = value
|
||||||
|
var vi: int = 0;
|
||||||
|
while vi < decl.variantCount && hm.constCount < 512 {
|
||||||
|
var vname: String = "";
|
||||||
|
if vi == 0 { vname = decl.variant0.name; }
|
||||||
|
if vi == 1 { vname = decl.variant1.name; }
|
||||||
|
if vi == 2 { vname = decl.variant2.name; }
|
||||||
|
if vi == 3 { vname = decl.variant3.name; }
|
||||||
|
if !String_Eq(vname, "") {
|
||||||
|
let ci: int = hm.constCount;
|
||||||
|
hm.consts[ci].name = String_Concat(String_Concat(decl.strValue, "_"), vname);
|
||||||
|
hm.consts[ci].value = vi;
|
||||||
|
hm.constCount = hm.constCount + 1;
|
||||||
|
}
|
||||||
|
vi = vi + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
decl = decl.childDecl2;
|
decl = decl.childDecl2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,10 @@ func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
|
|||||||
|
|
||||||
func Sema_IsNumeric(kind: int) -> bool {
|
func Sema_IsNumeric(kind: int) -> bool {
|
||||||
if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; }
|
if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; }
|
||||||
return kind >= tyInt8 && kind <= tyUInt64;
|
if kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt { return true; }
|
||||||
|
if kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt { return true; }
|
||||||
|
if kind == tyFloat32 || kind == tyFloat64 { return true; }
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sema_IsBool(kind: int) -> bool {
|
func Sema_IsBool(kind: int) -> bool {
|
||||||
@@ -328,9 +331,12 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
||||||
if retType != sema.currentRetType {
|
if retType != sema.currentRetType {
|
||||||
// Be permissive: allow numeric widening, pointer/named mismatch
|
// Be permissive: allow numeric widening, pointer compatibility
|
||||||
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
||||||
if retType != sema.currentRetType {
|
// Allow pointer-type compatibility (String <-> *T, *T <-> *U)
|
||||||
|
let retIsPtr: bool = retType == tyPointer || retType == tyStr;
|
||||||
|
let expectedIsPtr: bool = sema.currentRetType == tyPointer || sema.currentRetType == tyStr;
|
||||||
|
if !retIsPtr || !expectedIsPtr {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-9
@@ -440,6 +440,47 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
|||||||
StringBuilder_Append(&cbe.sb, ")");
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helpers for detecting generic declarations (not yet monomorphized)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBE_IsGenericTypeName(name: String) -> bool {
|
||||||
|
if String_Eq(name, "T") || String_Eq(name, "K") || String_Eq(name, "V") { return true; }
|
||||||
|
if String_Eq(name, "T*") || String_Eq(name, "K*") || String_Eq(name, "V*") { return true; }
|
||||||
|
// Generic container structs and their pointer variants
|
||||||
|
if String_Eq(name, "Array") || String_Eq(name, "Array*") { return true; }
|
||||||
|
if String_Eq(name, "SetEntry") || String_Eq(name, "SetEntry*") { return true; }
|
||||||
|
if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; }
|
||||||
|
if String_Eq(name, "MapEntry") || String_Eq(name, "MapEntry*") { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_StructHasGeneric(st: *HirStruct) -> bool {
|
||||||
|
var fi: int = 0;
|
||||||
|
while fi < st.fieldCount {
|
||||||
|
if CBE_IsGenericTypeName(st.fields[fi].typeName) { return true; }
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
|
||||||
|
var pi: int = 0;
|
||||||
|
while pi < f.paramCount {
|
||||||
|
var ptype: String = "";
|
||||||
|
if pi == 0 { ptype = f.param0.typeName; }
|
||||||
|
if pi == 1 { ptype = f.param1.typeName; }
|
||||||
|
if pi == 2 { ptype = f.param2.typeName; }
|
||||||
|
if pi == 3 { ptype = f.param3.typeName; }
|
||||||
|
if pi == 4 { ptype = f.param4.typeName; }
|
||||||
|
if pi == 5 { ptype = f.param5.typeName; }
|
||||||
|
if CBE_IsGenericTypeName(ptype) { return true; }
|
||||||
|
pi = pi + 1;
|
||||||
|
}
|
||||||
|
if CBE_IsGenericTypeName(f.retTypeName) { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Generate complete C module
|
// Generate complete C module
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -466,27 +507,62 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
|
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
|
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
|
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
|
||||||
StringBuilder_Append(&cbe.sb, "typedef double float64;\n\n");
|
StringBuilder_Append(&cbe.sb, "typedef double float64;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef char char8;\n\n");
|
||||||
// Runtime declarations
|
// Runtime declarations
|
||||||
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
|
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
|
||||||
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
|
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
|
||||||
|
|
||||||
// Forward declare all struct types
|
// Forward declare all struct types (skip generics)
|
||||||
var si: int = 0;
|
var si: int = 0;
|
||||||
while si < mod.structCount {
|
while si < mod.structCount {
|
||||||
|
if !CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
}
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Struct definitions
|
// Enum definitions (typedef to int)
|
||||||
|
var ei: int = 0;
|
||||||
|
while ei < mod.enumCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef int ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.enums[ei].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
ei = ei + 1;
|
||||||
|
}
|
||||||
|
if mod.enumCount > 0 {
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant definitions
|
||||||
|
var ci: int = 0;
|
||||||
|
while ci < mod.constCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "#define ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.consts[ci].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, String_FromInt(mod.consts[ci].value as int64));
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
if mod.constCount > 0 {
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct definitions (skip generics)
|
||||||
si = 0;
|
si = 0;
|
||||||
while si < mod.structCount {
|
while si < mod.structCount {
|
||||||
StringBuilder_Append(&cbe.sb, "typedef struct {\n");
|
if CBE_StructHasGeneric(&mod.structs[si]) {
|
||||||
|
si = si + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "struct ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
var fi: int = 0;
|
var fi: int = 0;
|
||||||
while fi < mod.structs[si].fieldCount {
|
while fi < mod.structs[si].fieldCount {
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
@@ -508,17 +584,17 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "} ");
|
StringBuilder_Append(&cbe.sb, "};\n\n");
|
||||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
|
||||||
StringBuilder_Append(&cbe.sb, ";\n\n");
|
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward declarations for all functions
|
// Forward declarations for all functions (skip generics)
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
|
if !CBE_FuncHasGeneric(&mod.funcs[i]) {
|
||||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
}
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
@@ -532,11 +608,16 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Function definitions
|
// Function definitions (skip generics)
|
||||||
var hasMain: bool = false;
|
var hasMain: bool = false;
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||||
|
// Skip generic functions
|
||||||
|
if CBE_FuncHasGeneric(&mod.funcs[i]) {
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Skip forward declarations (functions without body)
|
// Skip forward declarations (functions without body)
|
||||||
let body: *HirNode = mod.funcs[i].body;
|
let body: *HirNode = mod.funcs[i].body;
|
||||||
if body == null as *HirNode {
|
if body == null as *HirNode {
|
||||||
|
|||||||
+160
-96
@@ -1,5 +1,5 @@
|
|||||||
// cli.bux — CLI driver for the Bux self-hosting compiler
|
// cli.bux — CLI driver for the Bux self-hosting compiler
|
||||||
// Wires together: Lexer → Parser → Sema → HirLower → NimBackend
|
// Wires together: Lexer → Parser → Sema → HirLower → CBackend
|
||||||
module Cli {
|
module Cli {
|
||||||
|
|
||||||
extern func PrintLine(s: String);
|
extern func PrintLine(s: String);
|
||||||
@@ -97,14 +97,14 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
|||||||
Print("HIR funcCount=");
|
Print("HIR funcCount=");
|
||||||
PrintInt(hirMod.funcCount);
|
PrintInt(hirMod.funcCount);
|
||||||
|
|
||||||
// Phase 5: QBE SSA code generation
|
// Phase 5: C code generation
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
let cCode: String = CBackend_Generate(hirMod);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Lexer_Free(lex);
|
Lexer_Free(lex);
|
||||||
Sema_Free(sema);
|
Sema_Free(sema);
|
||||||
|
|
||||||
return qbeCode;
|
return cCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -122,23 +122,64 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
Print("Compiling ");
|
Print("Compiling ");
|
||||||
PrintLine(srcPath);
|
PrintLine(srcPath);
|
||||||
|
|
||||||
let ssaCode: String = Cli_Compile(source, srcPath);
|
let cCode: String = Cli_Compile(source, srcPath);
|
||||||
|
|
||||||
if String_Eq(ssaCode, "") {
|
if String_Eq(cCode, "") {
|
||||||
PrintLine("Compilation failed");
|
PrintLine("Compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write SSA output
|
// Write C output
|
||||||
let ok: bool = WriteFile(outPath, ssaCode);
|
let cFile: String = String_Concat(outPath, ".c");
|
||||||
if ok {
|
let ok: bool = WriteFile(cFile, cCode);
|
||||||
Print(" → SSA written to ");
|
if !ok {
|
||||||
PrintLine(outPath);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
PrintLine("Error: cannot write output file");
|
PrintLine("Error: cannot write output file");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Print(" → C written to ");
|
||||||
|
PrintLine(cFile);
|
||||||
|
|
||||||
|
// Find runtime.c and io.c for linking
|
||||||
|
var rtPath: String = "stdlib/runtime.c";
|
||||||
|
var ioPath: String = "stdlib/io.c";
|
||||||
|
if !FileExists(rtPath) {
|
||||||
|
rtPath = "../stdlib/runtime.c";
|
||||||
|
}
|
||||||
|
if !FileExists(ioPath) {
|
||||||
|
ioPath = "../stdlib/io.c";
|
||||||
|
}
|
||||||
|
if !FileExists(rtPath) {
|
||||||
|
rtPath = "/home/ziko/z-git/bux/bux/stdlib/runtime.c";
|
||||||
|
}
|
||||||
|
if !FileExists(ioPath) {
|
||||||
|
ioPath = "/home/ziko/z-git/bux/bux/stdlib/io.c";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile with cc
|
||||||
|
PrintLine("Compiling C...");
|
||||||
|
var cmdBuf: StringBuilder = StringBuilder_NewCap(512);
|
||||||
|
StringBuilder_Append(&cmdBuf, "cc -O2 -pthread -o ");
|
||||||
|
StringBuilder_Append(&cmdBuf, outPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
StringBuilder_Append(&cmdBuf, cFile);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
if FileExists(rtPath) {
|
||||||
|
StringBuilder_Append(&cmdBuf, rtPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
}
|
||||||
|
if FileExists(ioPath) {
|
||||||
|
StringBuilder_Append(&cmdBuf, ioPath);
|
||||||
|
StringBuilder_Append(&cmdBuf, " ");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cmdBuf, "-lm");
|
||||||
|
let ccRc: int = bux_system(StringBuilder_Build(&cmdBuf));
|
||||||
|
if ccRc != 0 {
|
||||||
|
PrintLine("Error: C compilation failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Print(" → Binary: ");
|
||||||
|
PrintLine(outPath);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -307,6 +348,55 @@ func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: convert single char int to String
|
||||||
|
func String_FromChar(c: int) -> String {
|
||||||
|
var buf: *char8 = bux_alloc(2) as *char8;
|
||||||
|
buf[0] = c as char8;
|
||||||
|
buf[1] = 0 as char8;
|
||||||
|
return buf as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect stdlib module paths imported by user code.
|
||||||
|
// Returns number of unique stdlib files to merge (paths written into outPaths).
|
||||||
|
func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, stdlibDir: String) -> int {
|
||||||
|
if mod == null as *Module { return 0; }
|
||||||
|
var count: int = 0;
|
||||||
|
var decl: *Decl = mod.firstItem;
|
||||||
|
while decl != null as *Decl && count < maxCount {
|
||||||
|
if decl.kind == dkUse {
|
||||||
|
let path: String = decl.usePath;
|
||||||
|
// Only handle Std::* imports
|
||||||
|
if String_StartsWith(path, "Std::") {
|
||||||
|
// Extract module name after Std:: (e.g., "Std::Io" -> "Io")
|
||||||
|
var j: int = 5; // skip "Std::"
|
||||||
|
var modName: String = "";
|
||||||
|
while j < 100 {
|
||||||
|
let c: int = path[j] as int;
|
||||||
|
if c == 0 || c == 58 { break; } // null or ':'
|
||||||
|
modName = String_Concat(modName, String_FromChar(c));
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if !String_Eq(modName, "") {
|
||||||
|
let filePath: String = bux_path_join(bux_path_join(stdlibDir, "Std"), String_Concat(modName, ".bux"));
|
||||||
|
// Check for duplicates
|
||||||
|
var dup: bool = false;
|
||||||
|
var di: int = 0;
|
||||||
|
while di < count {
|
||||||
|
if String_Eq(outPaths[di], filePath) { dup = true; }
|
||||||
|
di = di + 1;
|
||||||
|
}
|
||||||
|
if !dup {
|
||||||
|
outPaths[count] = filePath;
|
||||||
|
count = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decl = decl.childDecl2;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
|
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
|
||||||
Print("DEBUG: MergeFileInto ");
|
Print("DEBUG: MergeFileInto ");
|
||||||
PrintLine(path);
|
PrintLine(path);
|
||||||
@@ -473,33 +563,25 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
merged.itemCount = 0;
|
merged.itemCount = 0;
|
||||||
merged.firstItem = null as *Decl;
|
merged.firstItem = null as *Decl;
|
||||||
|
|
||||||
// Find and merge stdlib declarations
|
// Find and merge stdlib declarations (only imported modules)
|
||||||
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
||||||
if !String_Eq(stdlibDir, "") {
|
if !String_Eq(stdlibDir, "") {
|
||||||
Print("Stdlib found: ");
|
Print("Stdlib found: ");
|
||||||
PrintLine(stdlibDir);
|
PrintLine(stdlibDir);
|
||||||
let stdSubdir: String = bux_path_join(stdlibDir, "Std");
|
// Collect imported stdlib modules from user code
|
||||||
if DirExists(stdSubdir) {
|
let maxImports: int = 64;
|
||||||
var stdFileCount: int = 0;
|
let importPaths: *String = bux_alloc(maxImports * 8) as *String;
|
||||||
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
let importCount: int = Cli_CollectStdlibImports(userMerged, importPaths, maxImports, stdlibDir);
|
||||||
Print("DEBUG: stdFileCount=");
|
Print("Imported stdlib modules: ");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(importCount);
|
||||||
if stdFileCount > 0 {
|
PrintLine("");
|
||||||
Print("Merging ");
|
if importCount > 0 {
|
||||||
PrintInt(stdFileCount);
|
|
||||||
PrintLine(" stdlib files");
|
|
||||||
var si: int = 0;
|
var si: int = 0;
|
||||||
var stdAdded: int = 0;
|
var stdAdded: int = 0;
|
||||||
while si < stdFileCount {
|
while si < importCount {
|
||||||
Print("DEBUG: merging stdlib file ");
|
Print(" Merging ");
|
||||||
PrintInt(si);
|
PrintLine(importPaths[si]);
|
||||||
// Skip Channel.bux for debugging
|
let added: int = Cli_MergeFileInto(merged, importPaths[si], userNames, userNameCount);
|
||||||
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;
|
stdAdded = stdAdded + added;
|
||||||
si = si + 1;
|
si = si + 1;
|
||||||
}
|
}
|
||||||
@@ -507,7 +589,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
PrintInt(stdAdded);
|
PrintInt(stdAdded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Copy user declarations into merged (user shadows stdlib)
|
// Copy user declarations into merged (user shadows stdlib)
|
||||||
PrintLine("DEBUG: before copy");
|
PrintLine("DEBUG: before copy");
|
||||||
@@ -554,6 +635,14 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
let sema: *Sema = Sema_Analyze(merged);
|
let sema: *Sema = Sema_Analyze(merged);
|
||||||
if Sema_HasError(sema) {
|
if Sema_HasError(sema) {
|
||||||
PrintLine("Sema errors found");
|
PrintLine("Sema errors found");
|
||||||
|
var i: int = 0;
|
||||||
|
while i < Sema_DiagCount(sema) {
|
||||||
|
Print(" line ");
|
||||||
|
PrintInt(sema.diags[i].line as int64);
|
||||||
|
Print(": ");
|
||||||
|
PrintLine(sema.diags[i].message);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,85 +658,60 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
|
||||||
|
|
||||||
// QBE SSA code generation
|
// C code generation
|
||||||
PrintLine("Generating QBE SSA...");
|
PrintLine("Generating C code...");
|
||||||
var fi: int = 0;
|
let cCode: String = CBackend_Generate(hirMod);
|
||||||
while fi < hirMod.funcCount {
|
|
||||||
Print("func ");
|
|
||||||
PrintLine(hirMod.funcs[fi].name);
|
|
||||||
fi = fi + 1;
|
|
||||||
}
|
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
|
||||||
|
|
||||||
// Create build directory
|
// Create build directory
|
||||||
let buildDir: String = bux_path_join(projectDir, "build");
|
let buildDir: String = bux_path_join(projectDir, "build");
|
||||||
discard bux_mkdir_if_needed(buildDir);
|
discard bux_mkdir_if_needed(buildDir);
|
||||||
|
|
||||||
// Write SSA output
|
// Write C output
|
||||||
let ssaFile: String = bux_path_join(buildDir, "main.ssa");
|
let cFile: String = bux_path_join(buildDir, "main.c");
|
||||||
if !WriteFile(ssaFile, qbeCode) {
|
if !WriteFile(cFile, cCode) {
|
||||||
PrintLine("Error: cannot write main.ssa");
|
PrintLine("Error: cannot write main.c");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Print("C code written to ");
|
||||||
|
PrintLine(cFile);
|
||||||
|
|
||||||
Print("SSA written to ");
|
// Find runtime.c and io.c (use stdlibDir if available)
|
||||||
PrintLine(ssaFile);
|
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
||||||
|
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
||||||
// Invoke QBE to generate assembly
|
if !FileExists(rtPath) {
|
||||||
PrintLine("Invoking QBE...");
|
rtPath = bux_path_join(projectDir, "stdlib/runtime.c");
|
||||||
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) {
|
if !FileExists(ioPath) {
|
||||||
qbeCmd = "vendor/qbe/qbe";
|
ioPath = bux_path_join(projectDir, "stdlib/io.c");
|
||||||
}
|
}
|
||||||
// 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) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compile with cc
|
||||||
|
PrintLine("Compiling C...");
|
||||||
|
let outBin: String = bux_path_join(buildDir, outName);
|
||||||
|
var ccBuf: StringBuilder = StringBuilder_NewCap(512);
|
||||||
|
StringBuilder_Append(&ccBuf, "cc -O2 -pthread -o ");
|
||||||
|
StringBuilder_Append(&ccBuf, outBin);
|
||||||
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
|
StringBuilder_Append(&ccBuf, cFile);
|
||||||
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
if FileExists(rtPath) {
|
if FileExists(rtPath) {
|
||||||
StringBuilder_Append(&linkBuf, rtPath);
|
StringBuilder_Append(&ccBuf, rtPath);
|
||||||
StringBuilder_Append(&linkBuf, " ");
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
}
|
}
|
||||||
if FileExists(ioPath) {
|
if FileExists(ioPath) {
|
||||||
StringBuilder_Append(&linkBuf, ioPath);
|
StringBuilder_Append(&ccBuf, ioPath);
|
||||||
StringBuilder_Append(&linkBuf, " ");
|
StringBuilder_Append(&ccBuf, " ");
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&linkBuf, "-lm");
|
StringBuilder_Append(&ccBuf, "-lm");
|
||||||
let linkRc: int = bux_system(StringBuilder_Build(&linkBuf));
|
let ccRc: int = bux_system(StringBuilder_Build(&ccBuf));
|
||||||
if linkRc != 0 {
|
if ccRc != 0 {
|
||||||
PrintLine("Error: Linking failed");
|
PrintLine("Error: C compilation failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +757,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
PrintLine(" Parser ✅ 1004 lines");
|
PrintLine(" Parser ✅ 1004 lines");
|
||||||
PrintLine(" Sema ✅ 393 lines");
|
PrintLine(" Sema ✅ 393 lines");
|
||||||
PrintLine(" HirLower ✅ 307 lines");
|
PrintLine(" HirLower ✅ 307 lines");
|
||||||
PrintLine(" CBackend ✅ 264 lines");
|
PrintLine(" CBackend ✅ 585 lines");
|
||||||
PrintLine(" Total: 3830 lines of Bux");
|
PrintLine(" Total: 3830 lines of Bux");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -716,7 +780,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
|
|
||||||
if String_Eq(cmd, "build") {
|
if String_Eq(cmd, "build") {
|
||||||
let src: String = "src/Main.bux";
|
let src: String = "src/Main.bux";
|
||||||
let out: String = "build/main.nim";
|
let out: String = "build/main";
|
||||||
if argCount >= 3 { src = args[2]; }
|
if argCount >= 3 { src = args[2]; }
|
||||||
if argCount >= 4 { out = args[3]; }
|
if argCount >= 4 { out = args[3]; }
|
||||||
return Cli_Build(src, out);
|
return Cli_Build(src, out);
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ struct HirConst {
|
|||||||
value: int;
|
value: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct HirEnum {
|
||||||
|
name: String;
|
||||||
|
}
|
||||||
|
|
||||||
struct HirModule {
|
struct HirModule {
|
||||||
funcCount: int;
|
funcCount: int;
|
||||||
funcs: *HirFunc;
|
funcs: *HirFunc;
|
||||||
@@ -123,6 +127,7 @@ struct HirModule {
|
|||||||
structCount: int;
|
structCount: int;
|
||||||
structs: *HirStruct;
|
structs: *HirStruct;
|
||||||
enumCount: int;
|
enumCount: int;
|
||||||
|
enums: *HirEnum;
|
||||||
constCount: int;
|
constCount: int;
|
||||||
consts: *HirConst;
|
consts: *HirConst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,16 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
|
|
||||||
// Field access
|
// Field access
|
||||||
if kind == ekField {
|
if kind == ekField {
|
||||||
|
// Check if this is enum variant access: Color::Green
|
||||||
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
|
||||||
|
if sym.decl != null as *Decl && sym.decl.kind == dkEnum {
|
||||||
|
// Emit as variable reference: Color_Green
|
||||||
|
n.kind = hVar;
|
||||||
|
n.strValue = String_Concat(String_Concat(expr.child1.strValue, "_"), expr.strValue);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
n.kind = hFieldPtr;
|
n.kind = hFieldPtr;
|
||||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
n.strValue = expr.strValue;
|
n.strValue = expr.strValue;
|
||||||
@@ -543,6 +553,8 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
hm.funcs = ctx.funcs;
|
hm.funcs = ctx.funcs;
|
||||||
hm.structCount = 0;
|
hm.structCount = 0;
|
||||||
hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
|
hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
|
||||||
|
hm.enumCount = 0;
|
||||||
|
hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
|
||||||
hm.constCount = 0;
|
hm.constCount = 0;
|
||||||
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
|
||||||
|
|
||||||
@@ -628,7 +640,25 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
hm.constCount = hm.constCount + 1;
|
hm.constCount = hm.constCount + 1;
|
||||||
}
|
}
|
||||||
if decl.kind == dkEnum {
|
if decl.kind == dkEnum {
|
||||||
|
let ei: int = hm.enumCount;
|
||||||
|
hm.enums[ei].name = decl.strValue;
|
||||||
hm.enumCount = hm.enumCount + 1;
|
hm.enumCount = hm.enumCount + 1;
|
||||||
|
// Emit enum variants as constants: EnumName_VariantName = value
|
||||||
|
var vi: int = 0;
|
||||||
|
while vi < decl.variantCount && hm.constCount < 512 {
|
||||||
|
var vname: String = "";
|
||||||
|
if vi == 0 { vname = decl.variant0.name; }
|
||||||
|
if vi == 1 { vname = decl.variant1.name; }
|
||||||
|
if vi == 2 { vname = decl.variant2.name; }
|
||||||
|
if vi == 3 { vname = decl.variant3.name; }
|
||||||
|
if !String_Eq(vname, "") {
|
||||||
|
let ci: int = hm.constCount;
|
||||||
|
hm.consts[ci].name = String_Concat(String_Concat(decl.strValue, "_"), vname);
|
||||||
|
hm.consts[ci].value = vi;
|
||||||
|
hm.constCount = hm.constCount + 1;
|
||||||
|
}
|
||||||
|
vi = vi + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
decl = decl.childDecl2;
|
decl = decl.childDecl2;
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -96,7 +96,10 @@ func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
|
|||||||
|
|
||||||
func Sema_IsNumeric(kind: int) -> bool {
|
func Sema_IsNumeric(kind: int) -> bool {
|
||||||
if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; }
|
if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; }
|
||||||
return kind >= tyInt8 && kind <= tyUInt64;
|
if kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt { return true; }
|
||||||
|
if kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt { return true; }
|
||||||
|
if kind == tyFloat32 || kind == tyFloat64 { return true; }
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sema_IsBool(kind: int) -> bool {
|
func Sema_IsBool(kind: int) -> bool {
|
||||||
@@ -328,9 +331,12 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
||||||
if retType != sema.currentRetType {
|
if retType != sema.currentRetType {
|
||||||
// Be permissive: allow numeric widening, pointer/named mismatch
|
// Be permissive: allow numeric widening, pointer compatibility
|
||||||
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
||||||
if retType != sema.currentRetType {
|
// Allow pointer-type compatibility (String <-> *T, *T <-> *U)
|
||||||
|
let retIsPtr: bool = retType == tyPointer || retType == tyStr;
|
||||||
|
let expectedIsPtr: bool = sema.currentRetType == tyPointer || sema.currentRetType == tyStr;
|
||||||
|
if !retIsPtr || !expectedIsPtr {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user