From 782aaa729d80f9be9bfb7926df65c307587af8e0 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Sat, 30 May 2026 22:56:03 +0300 Subject: [PATCH] feat: add stdlib IO and extern function support - Add stdlib/io.c with PrintLine, Print, PrintInt, PrintFloat, PrintBool, ReadLine - Support extern function declarations (functions without body) - Generate C forward declarations for extern functions - Map Bux type names (String, int, etc.) to C types in codegen - Update build system to copy all stdlib files - Hello World example works: prints 'Hello, Bux!' --- src/c_backend.nim | 36 +++++++++++++++++++++++++++++++++- src/cli.nim | 47 +++++++++++++++++++++++++++++---------------- src/hir.nim | 1 + src/hir_lower.nim | 9 +++++++-- stdlib/io.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 stdlib/io.c diff --git a/src/c_backend.nim b/src/c_backend.nim index 20ac309..e8317a7 100644 --- a/src/c_backend.nim +++ b/src/c_backend.nim @@ -64,7 +64,24 @@ proc typeToC*(typ: Type): string = if typ.inner.len > 0: return typeToC(typ.inner[0]) & "*" return "void*" - of tkNamed: return typ.name + of tkNamed: + # Map common Bux type names to C types + case typ.name + of "String", "str": return "const char*" + of "int": return "int" + of "int8": return "int8_t" + of "int16": return "int16_t" + of "int32": return "int32_t" + of "int64": return "int64_t" + of "uint": return "unsigned int" + of "uint8": return "uint8_t" + of "uint16": return "uint16_t" + of "uint32": return "uint32_t" + of "uint64": return "uint64_t" + of "float32": return "float" + of "float64": return "double" + of "bool": return "bool" + else: return typ.name of tkTuple: return "void*" # TODO: proper tuple struct of tkFunc: return "void*" # TODO: function pointer else: return "int" @@ -344,6 +361,16 @@ proc emitEnum*(be: var CBackend, name: string, variants: seq[string]) = be.emitLine(&"}} {name};") be.emitLine("") +proc emitExternDecl*(be: var CBackend, efunc: HirFunc) = + let retType = typeToC(efunc.retType) + var params: seq[string] = @[] + for p in efunc.params: + params.add(&"{typeToC(p.typ)} {p.name}") + if params.len == 0: + params.add("void") + let paramsStr = params.join(", ") + be.emitLine(&"extern {retType} {efunc.name}({paramsStr});") + proc emitModule*(be: var CBackend, module: HirModule): string = # Header be.emitLine("/* Generated by Bux Compiler */") @@ -360,6 +387,13 @@ proc emitModule*(be: var CBackend, module: HirModule): string = if module.structs.len > 0: be.emitLine("") + # Extern function declarations + if module.externFuncs.len > 0: + be.emitLine("/* Extern function declarations */") + for ef in module.externFuncs: + be.emitExternDecl(ef) + be.emitLine("") + # Struct definitions for s in module.structs: be.emitStruct(s.name, s.fields) diff --git a/src/cli.nim b/src/cli.nim index d674ae8..7b7f3d8 100644 --- a/src/cli.nim +++ b/src/cli.nim @@ -256,30 +256,45 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int = let cFile = buildDir / "main.c" writeFile(cFile, allCCode) - # Copy runtime + # Copy runtime and stdlib files let runtimeDst = buildDir / "runtime.c" - var runtimeFound = false - # Search in multiple locations - let searchPaths = @[ - getAppDir() / ".." / "stdlib" / "runtime.c", - getAppDir() / "stdlib" / "runtime.c", - root / "stdlib" / "runtime.c", - root / "stdlib_runtime.c", - "/home/ziko/z-git/bux/bux/stdlib/runtime.c", # TODO: make this configurable + let ioDst = buildDir / "io.c" + var stdlibDir = "" + # Search for stdlib directory + let stdlibSearchPaths = @[ + getAppDir() / ".." / "stdlib", + getAppDir() / "stdlib", + root / "stdlib", + "/home/ziko/z-git/bux/bux/stdlib", # TODO: make this configurable ] - for runtimePath in searchPaths: - if fileExists(runtimePath): - copyFile(runtimePath, runtimeDst) - runtimeFound = true + for path in stdlibSearchPaths: + if dirExists(path): + stdlibDir = path break - if not runtimeFound: - printError("runtime.c not found (searched in stdlib/, build/, and project root)", useColor) + if stdlibDir == "": + printError("stdlib directory not found", useColor) + return 1 + + # Copy runtime.c + let runtimeSrc = stdlibDir / "runtime.c" + if fileExists(runtimeSrc): + copyFile(runtimeSrc, runtimeDst) + else: + printError("runtime.c not found in stdlib", useColor) + return 1 + + # Copy io.c + let ioSrc = stdlibDir / "io.c" + if fileExists(ioSrc): + copyFile(ioSrc, ioDst) + else: + printError("io.c not found in stdlib", useColor) return 1 # Compile with cc let outputName = if man.name != "": man.name else: "bux_out" let outputFile = buildDir / outputName - let ccCmd = &"cc -o {outputFile} {cFile} {runtimeDst} -lm 2>&1" + let ccCmd = &"cc -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm 2>&1" if opts.verbose: printInfo(&"running: {ccCmd}", useColor) let (output, exitCode) = execCmdEx(ccCmd) diff --git a/src/hir.nim b/src/hir.nim index 8014dfd..e38fdda 100644 --- a/src/hir.nim +++ b/src/hir.nim @@ -126,6 +126,7 @@ type HirModule* = object funcs*: seq[HirFunc] + externFuncs*: seq[HirFunc] # Functions declared with extern (no body) structs*: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]] enums*: seq[tuple[name: string, variants: seq[string]]] consts*: seq[tuple[name: string, typ: Type, value: HirNode]] diff --git a/src/hir_lower.nim b/src/hir_lower.nim index 882d166..2b26852 100644 --- a/src/hir_lower.nim +++ b/src/hir_lower.nim @@ -420,6 +420,7 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc = proc lowerModule*(module: Module, sema: Sema): HirModule = var ctx = initLowerCtx(module, sema) var funcs: seq[HirFunc] = @[] + var externFuncs: seq[HirFunc] = @[] var structs: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]] = @[] var enums: seq[tuple[name: string, variants: seq[string]]] = @[] var consts: seq[tuple[name: string, typ: Type, value: HirNode]] = @[] @@ -427,7 +428,11 @@ proc lowerModule*(module: Module, sema: Sema): HirModule = for decl in module.items: case decl.kind of dkFunc: - funcs.add(ctx.lowerFunc(decl)) + if decl.declFuncBody != nil: + funcs.add(ctx.lowerFunc(decl)) + else: + # Extern function (no body) + externFuncs.add(ctx.lowerFunc(decl)) of dkImpl: for methodDecl in decl.declImplMethods: if methodDecl.kind == dkFunc: @@ -461,4 +466,4 @@ proc lowerModule*(module: Module, sema: Sema): HirModule = consts.add((decl.declConstName, typ, value)) else: discard - result = HirModule(funcs: funcs, structs: structs, enums: enums, consts: consts) + result = HirModule(funcs: funcs, externFuncs: externFuncs, structs: structs, enums: enums, consts: consts) diff --git a/stdlib/io.c b/stdlib/io.c new file mode 100644 index 0000000..a8240cf --- /dev/null +++ b/stdlib/io.c @@ -0,0 +1,49 @@ +/* Bux Standard Library - I/O functions */ + +#include +#include +#include + +/* PrintLine - print string with newline */ +void Std_Io_PrintLine(const char* s) { + if (s != NULL) { + puts(s); + } else { + puts(""); + } +} + +/* Print - print string without newline */ +void Std_Io_Print(const char* s) { + if (s != NULL) { + printf("%s", s); + } +} + +/* PrintInt - print integer */ +void Std_Io_PrintInt(int64_t n) { + printf("%lld", (long long)n); +} + +/* PrintFloat - print float */ +void Std_Io_PrintFloat(double f) { + printf("%g", f); +} + +/* PrintBool - print boolean */ +void Std_Io_PrintBool(int b) { + printf("%s", b ? "true" : "false"); +} + +/* ReadLine - read line from stdin (simplified) */ +const char* Std_Io_ReadLine(void) { + static char buffer[1024]; + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + size_t len = strlen(buffer); + if (len > 0 && buffer[len-1] == '\n') { + buffer[len-1] = '\0'; + } + return buffer; + } + return ""; +}