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:
+35
-1
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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]]
|
||||||
|
|||||||
+7
-2
@@ -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:
|
||||||
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:
|
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
@@ -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 "";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user