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!'
This commit is contained in:
2026-05-30 22:56:03 +03:00
parent 8e74215378
commit 782aaa729d
5 changed files with 123 additions and 19 deletions
+35 -1
View File
@@ -64,7 +64,24 @@ proc typeToC*(typ: Type): string =
if typ.inner.len > 0: if typ.inner.len > 0:
return typeToC(typ.inner[0]) & "*" return typeToC(typ.inner[0]) & "*"
return "void*" 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 tkTuple: return "void*" # TODO: proper tuple struct
of tkFunc: return "void*" # TODO: function pointer of tkFunc: return "void*" # TODO: function pointer
else: return "int" else: return "int"
@@ -344,6 +361,16 @@ proc emitEnum*(be: var CBackend, name: string, variants: seq[string]) =
be.emitLine(&"}} {name};") be.emitLine(&"}} {name};")
be.emitLine("") 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 = proc emitModule*(be: var CBackend, module: HirModule): string =
# Header # Header
be.emitLine("/* Generated by Bux Compiler */") be.emitLine("/* Generated by Bux Compiler */")
@@ -360,6 +387,13 @@ proc emitModule*(be: var CBackend, module: HirModule): string =
if module.structs.len > 0: if module.structs.len > 0:
be.emitLine("") 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 # Struct definitions
for s in module.structs: for s in module.structs:
be.emitStruct(s.name, s.fields) be.emitStruct(s.name, s.fields)
+31 -16
View File
@@ -256,30 +256,45 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
let cFile = buildDir / "main.c" let cFile = buildDir / "main.c"
writeFile(cFile, allCCode) writeFile(cFile, allCCode)
# Copy runtime # Copy runtime and stdlib files
let runtimeDst = buildDir / "runtime.c" let runtimeDst = buildDir / "runtime.c"
var runtimeFound = false let ioDst = buildDir / "io.c"
# Search in multiple locations var stdlibDir = ""
let searchPaths = @[ # Search for stdlib directory
getAppDir() / ".." / "stdlib" / "runtime.c", let stdlibSearchPaths = @[
getAppDir() / "stdlib" / "runtime.c", getAppDir() / ".." / "stdlib",
root / "stdlib" / "runtime.c", getAppDir() / "stdlib",
root / "stdlib_runtime.c", root / "stdlib",
"/home/ziko/z-git/bux/bux/stdlib/runtime.c", # TODO: make this configurable "/home/ziko/z-git/bux/bux/stdlib", # TODO: make this configurable
] ]
for runtimePath in searchPaths: for path in stdlibSearchPaths:
if fileExists(runtimePath): if dirExists(path):
copyFile(runtimePath, runtimeDst) stdlibDir = path
runtimeFound = true
break break
if not runtimeFound: if stdlibDir == "":
printError("runtime.c not found (searched in stdlib/, build/, and project root)", useColor) 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 return 1
# Compile with cc # Compile with cc
let outputName = if man.name != "": man.name else: "bux_out" let outputName = if man.name != "": man.name else: "bux_out"
let outputFile = buildDir / outputName 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: if opts.verbose:
printInfo(&"running: {ccCmd}", useColor) printInfo(&"running: {ccCmd}", useColor)
let (output, exitCode) = execCmdEx(ccCmd) let (output, exitCode) = execCmdEx(ccCmd)
+1
View File
@@ -126,6 +126,7 @@ type
HirModule* = object HirModule* = object
funcs*: seq[HirFunc] funcs*: seq[HirFunc]
externFuncs*: seq[HirFunc] # Functions declared with extern (no body)
structs*: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]] structs*: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]]
enums*: seq[tuple[name: string, variants: seq[string]]] enums*: seq[tuple[name: string, variants: seq[string]]]
consts*: seq[tuple[name: string, typ: Type, value: HirNode]] consts*: seq[tuple[name: string, typ: Type, value: HirNode]]
+6 -1
View File
@@ -420,6 +420,7 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
proc lowerModule*(module: Module, sema: Sema): HirModule = proc lowerModule*(module: Module, sema: Sema): HirModule =
var ctx = initLowerCtx(module, sema) var ctx = initLowerCtx(module, sema)
var funcs: seq[HirFunc] = @[] var funcs: seq[HirFunc] = @[]
var externFuncs: seq[HirFunc] = @[]
var structs: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]] = @[] var structs: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]] = @[]
var enums: seq[tuple[name: string, variants: seq[string]]] = @[] var enums: seq[tuple[name: string, variants: seq[string]]] = @[]
var consts: seq[tuple[name: string, typ: Type, value: HirNode]] = @[] 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: for decl in module.items:
case decl.kind case decl.kind
of dkFunc: of dkFunc:
if decl.declFuncBody != nil:
funcs.add(ctx.lowerFunc(decl)) funcs.add(ctx.lowerFunc(decl))
else:
# Extern function (no body)
externFuncs.add(ctx.lowerFunc(decl))
of dkImpl: of dkImpl:
for methodDecl in decl.declImplMethods: for methodDecl in decl.declImplMethods:
if methodDecl.kind == dkFunc: if methodDecl.kind == dkFunc:
@@ -461,4 +466,4 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
consts.add((decl.declConstName, typ, value)) consts.add((decl.declConstName, typ, value))
else: discard else: discard
result = HirModule(funcs: funcs, structs: structs, enums: enums, consts: consts) result = HirModule(funcs: funcs, externFuncs: externFuncs, structs: structs, enums: enums, consts: consts)
+49
View File
@@ -0,0 +1,49 @@
/* Bux Standard Library - I/O functions */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/* 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 "";
}