ac969b37c1
- src/ ← compiler/selfhost/ (canonical Bux compiler) - bootstrap/ ← compiler/bootstrap/ (Nim bootstrap) - lib/ ← library/std/ (standard library) - rt/ ← library/runtime/ (C runtime) - tests/ ← compiler/tests/ (unit tests) - Remove _selfhost/ (built into build/selfhost/ now) - Update all path references (Makefile, cli.nim, cli.bux, docs) - Bump version to 0.3.0
722 lines
22 KiB
Nim
722 lines
22 KiB
Nim
import std/[strformat, strutils]
|
|
import hir, types, token
|
|
|
|
type
|
|
CBackend* = object
|
|
output*: string
|
|
indent*: int
|
|
varCounter*: int
|
|
declaredVars*: seq[string]
|
|
sliceTypeDefs*: seq[tuple[name: string, elem: string]] ## Generated Slice_<T> typedefs
|
|
|
|
proc cEscape(s: string): string =
|
|
## Escape a string for use as a C string literal.
|
|
result = ""
|
|
for c in s:
|
|
case c
|
|
of '\\': result.add("\\\\")
|
|
of '"': result.add("\\\"")
|
|
of '\n': result.add("\\n")
|
|
of '\r': result.add("\\r")
|
|
of '\t': result.add("\\t")
|
|
of '\0': result.add("\\0")
|
|
else: result.add(c)
|
|
|
|
proc initCBackend*(): CBackend =
|
|
result.output = ""
|
|
result.indent = 0
|
|
result.varCounter = 0
|
|
result.declaredVars = @[]
|
|
|
|
proc emit(be: var CBackend, s: string) =
|
|
be.output.add(s)
|
|
|
|
proc emitLine(be: var CBackend, s: string) =
|
|
for i in 0..<be.indent:
|
|
be.output.add(" ")
|
|
be.output.add(s)
|
|
be.output.add("\n")
|
|
|
|
proc emitIndent(be: var CBackend) =
|
|
for i in 0..<be.indent:
|
|
be.output.add(" ")
|
|
|
|
proc freshVar(be: var CBackend): string =
|
|
inc be.varCounter
|
|
result = &"__tmp_{be.varCounter}"
|
|
|
|
# Type conversion: Bux Type → C type string
|
|
proc typeToC*(be: var CBackend, typ: Type): string =
|
|
if typ == nil: return "void"
|
|
case typ.kind
|
|
of tkVoid: return "void"
|
|
of tkBool: return "bool"
|
|
of tkBool8: return "bool"
|
|
of tkBool16: return "bool"
|
|
of tkBool32: return "bool"
|
|
of tkChar8: return "char"
|
|
of tkChar16: return "char16_t"
|
|
of tkChar32: return "char32_t"
|
|
of tkStr: return "const char*"
|
|
of tkInt8: return "int8_t"
|
|
of tkInt16: return "int16_t"
|
|
of tkInt32: return "int32_t"
|
|
of tkInt64: return "int64_t"
|
|
of tkInt: return "int"
|
|
of tkUInt8: return "uint8_t"
|
|
of tkUInt16: return "uint16_t"
|
|
of tkUInt32: return "uint32_t"
|
|
of tkUInt64: return "uint64_t"
|
|
of tkUInt: return "unsigned int"
|
|
of tkFloat32: return "float"
|
|
of tkFloat64: return "double"
|
|
of tkPointer, tkRef, tkMutRef:
|
|
if typ.inner.len > 0:
|
|
return typeToC(be, typ.inner[0]) & "*"
|
|
return "void*"
|
|
of tkDynRef:
|
|
return typ.name & "_FatPtr"
|
|
of tkSlice:
|
|
let elemName = if typ.inner.len > 0: typeToC(be, typ.inner[0]) else: "void"
|
|
let sliceName = "Slice_" & elemName.replace(" ", "_").replace("*", "Ptr")
|
|
var alreadyDefined = false
|
|
for d in be.sliceTypeDefs:
|
|
if d.name == sliceName:
|
|
alreadyDefined = true
|
|
break
|
|
if not alreadyDefined:
|
|
be.sliceTypeDefs.add((name: sliceName, elem: elemName))
|
|
return sliceName
|
|
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:
|
|
when defined(release):
|
|
return "void*"
|
|
else:
|
|
stderr.writeLine("warning: C backend: unknown type kind " & $typ.kind & ", using void*")
|
|
return "void*"
|
|
|
|
proc operatorToC(op: TokenKind): string =
|
|
case op
|
|
of tkPlus: return "+"
|
|
of tkMinus: return "-"
|
|
of tkStar: return "*"
|
|
of tkSlash: return "/"
|
|
of tkPercent: return "%"
|
|
of tkAmp: return "&"
|
|
of tkPipe: return "|"
|
|
of tkCaret: return "^"
|
|
of tkShl: return "<<"
|
|
of tkShr: return ">>"
|
|
of tkAmpAmp: return "&&"
|
|
of tkPipePipe: return "||"
|
|
of tkEq: return "=="
|
|
of tkNe: return "!="
|
|
of tkLt: return "<"
|
|
of tkLe: return "<="
|
|
of tkGt: return ">"
|
|
of tkGe: return ">="
|
|
of tkBang: return "!"
|
|
of tkTilde: return "~"
|
|
of tkPlusPlus: return "++"
|
|
of tkMinusMinus: return "--"
|
|
of tkAssign: return "="
|
|
of tkPlusAssign: return "+="
|
|
of tkMinusAssign: return "-="
|
|
of tkStarAssign: return "*="
|
|
of tkSlashAssign: return "/="
|
|
of tkPercentAssign: return "%="
|
|
of tkAmpAssign: return "&="
|
|
of tkPipeAssign: return "|="
|
|
of tkCaretAssign: return "^="
|
|
of tkShlAssign: return "<<="
|
|
of tkShrAssign: return ">>="
|
|
else: return "?"
|
|
|
|
# Forward declaration
|
|
proc emitExpr(be: var CBackend, node: HirNode): string
|
|
proc emitStmt(be: var CBackend, node: HirNode)
|
|
|
|
proc emitExpr(be: var CBackend, node: HirNode): string =
|
|
if node == nil: return "0"
|
|
case node.kind
|
|
of hLit:
|
|
case node.litToken.kind
|
|
of tkBoolLiteral:
|
|
if node.litToken.text == "true": return "true"
|
|
else: return "false"
|
|
of tkStringLiteral:
|
|
var text = node.litToken.text
|
|
# Backtick raw string: strip backticks, escape content for C
|
|
if text.len >= 2 and text[0] == '`' and text[text.len-1] == '`':
|
|
text = "\"" & cEscape(text[1 ..< text.len-1]) & "\""
|
|
# If text has no surrounding quotes, it's from constFoldConstDecl (already unescaped)
|
|
elif text.len >= 2 and text[0] == '"' and text[text.len-1] == '"':
|
|
# Strip c8" c16" c32" prefixes — in C they are just regular string literals
|
|
if text.startsWith("c32\""):
|
|
text = "\"" & cEscape(text[4 ..< text.len-1]) & "\""
|
|
elif text.startsWith("c16\""):
|
|
text = "\"" & cEscape(text[4 ..< text.len-1]) & "\""
|
|
elif text.startsWith("c8\""):
|
|
text = "\"" & cEscape(text[3 ..< text.len-1]) & "\""
|
|
else:
|
|
text = "\"" & cEscape(text[1 ..< text.len-1]) & "\""
|
|
elif text.len >= 2 and text[0] == '"':
|
|
# Partial quote — escape anyway
|
|
text = "\"" & cEscape(text[1 ..< text.len]) & "\""
|
|
else:
|
|
# No quotes — from constFoldConstDecl, needs wrapping and escaping
|
|
text = "\"" & cEscape(text) & "\""
|
|
return text
|
|
of tkNull:
|
|
return "NULL"
|
|
else:
|
|
return node.litToken.text
|
|
|
|
of hVar:
|
|
return node.varName
|
|
|
|
of hSelf:
|
|
return "self"
|
|
|
|
of hUnary:
|
|
let operand = be.emitExpr(node.unaryOperand)
|
|
let op = operatorToC(node.unaryOp)
|
|
if node.unaryOp == tkStar:
|
|
return &"(*{operand})"
|
|
elif node.unaryOp == tkAmp:
|
|
return &"(&{operand})"
|
|
else:
|
|
return &"({op}{operand})"
|
|
|
|
of hBinary:
|
|
let left = be.emitExpr(node.binaryLeft)
|
|
let right = be.emitExpr(node.binaryRight)
|
|
let op = operatorToC(node.binaryOp)
|
|
return &"({left} {op} {right})"
|
|
|
|
of hCall:
|
|
var args: seq[string] = @[]
|
|
for arg in node.callArgs:
|
|
args.add(be.emitExpr(arg))
|
|
let argsStr = args.join(", ")
|
|
return &"{node.callCallee}({argsStr})"
|
|
|
|
of hCallIndirect:
|
|
let callee = be.emitExpr(node.callIndirectCallee)
|
|
var args: seq[string] = @[]
|
|
for arg in node.callIndirectArgs:
|
|
args.add(be.emitExpr(arg))
|
|
let argsStr = args.join(", ")
|
|
return &"({callee})({argsStr})"
|
|
|
|
of hLoad:
|
|
# Optimize: load(field_ptr(base, field)) → base.field (avoids & on temporaries)
|
|
if node.loadPtr != nil and node.loadPtr.kind == hFieldPtr:
|
|
let base = be.emitExpr(node.loadPtr.fieldPtrBase)
|
|
return &"({base}.{node.loadPtr.fieldName})"
|
|
# Optimize: load(arrow_field(base, field)) → base->field
|
|
if node.loadPtr != nil and node.loadPtr.kind == hArrowField:
|
|
let base = be.emitExpr(node.loadPtr.arrowFieldBase)
|
|
return &"({base}->{node.loadPtr.arrowFieldName})"
|
|
# Optimize: load(index_ptr(base, idx)) → base[idx]
|
|
if node.loadPtr != nil and node.loadPtr.kind == hIndexPtr:
|
|
let base = be.emitExpr(node.loadPtr.indexPtrBase)
|
|
let idx = be.emitExpr(node.loadPtr.indexPtrIndex)
|
|
return &"({base}[{idx}])"
|
|
let ptrExpr = be.emitExpr(node.loadPtr)
|
|
return &"(*{ptrExpr})"
|
|
|
|
of hFieldPtr:
|
|
let base = be.emitExpr(node.fieldPtrBase)
|
|
return &"(&({base}.{node.fieldName}))"
|
|
|
|
of hArrowField:
|
|
let base = be.emitExpr(node.arrowFieldBase)
|
|
return &"(&({base}->{node.arrowFieldName}))"
|
|
|
|
of hIndexPtr:
|
|
let base = be.emitExpr(node.indexPtrBase)
|
|
let idx = be.emitExpr(node.indexPtrIndex)
|
|
return &"(&({base}[{idx}]))"
|
|
|
|
of hStructInit:
|
|
# C99 compound literal: (StructName){.field1 = val1, .field2 = val2}
|
|
var fields: seq[string] = @[]
|
|
for f in node.structInitFields:
|
|
let val = be.emitExpr(f.value)
|
|
fields.add(&".{f.name} = {val}")
|
|
let fieldsStr = fields.join(", ")
|
|
return &"(({node.structInitName}){{{fieldsStr}}})"
|
|
|
|
of hSliceInit:
|
|
let sliceName = typeToC(be, node.typ)
|
|
var elems: seq[string] = @[]
|
|
for e in node.sliceInitElements:
|
|
elems.add(be.emitExpr(e))
|
|
let elemsStr = elems.join(", ")
|
|
let elemType = if node.typ.inner.len > 0: typeToC(be, node.typ.inner[0]) else: "void"
|
|
return &"({sliceName}){{.data = ({elemType}[]){{{elemsStr}}}, .len = {node.sliceInitLen}}}"
|
|
of hSliceIndex:
|
|
let base = be.emitExpr(node.sliceIndexBase)
|
|
let idx = be.emitExpr(node.sliceIndexIndex)
|
|
if node.sliceIndexBoundsCheck:
|
|
return &"(bux_bounds_check((size_t)({idx}), ({base}).len), ({base}).data[{idx}])"
|
|
else:
|
|
return &"({base}).data[{idx}]"
|
|
|
|
of hTupleInit:
|
|
var elems: seq[string] = @[]
|
|
for e in node.tupleInitElements:
|
|
elems.add(be.emitExpr(e))
|
|
return &"{{{elems.join(\", \")}}}"
|
|
|
|
of hCast:
|
|
let operand = be.emitExpr(node.castOperand)
|
|
let typ = typeToC(be, node.castType)
|
|
return &"(({typ}){operand})"
|
|
|
|
of hIs:
|
|
return "true" # TODO: proper type checking
|
|
|
|
of hSizeOf:
|
|
let typ = typeToC(be, node.sizeOfType)
|
|
return &"sizeof({typ})"
|
|
|
|
of hSpawn:
|
|
if node.spawnAsync:
|
|
# Async coroutine spawn
|
|
return &"bux_async_spawn({node.spawnCallee})"
|
|
else:
|
|
# OS thread spawn
|
|
var argsStr = "NULL"
|
|
if node.spawnArgs.len > 0:
|
|
argsStr = &"(void*){be.emitExpr(node.spawnArgs[0])}"
|
|
return &"bux_task_spawn((void* (*)(void*)){node.spawnCallee}, {argsStr})"
|
|
|
|
of hDynRef:
|
|
let data = be.emitExpr(node.dynRefData)
|
|
let iface = node.dynRefInterface
|
|
let concrete = node.dynRefConcreteType
|
|
return &"({iface}_FatPtr){{.data = {data}, .vtable = &{concrete}_{iface}_VTable}}"
|
|
|
|
of hDynCall:
|
|
let receiver = be.emitExpr(node.dynCallReceiver)
|
|
let methodName = node.dynCallMethod
|
|
var args: seq[string] = @[]
|
|
args.add(&"{receiver}.data")
|
|
for i in 1 ..< node.dynCallArgs.len:
|
|
args.add(be.emitExpr(node.dynCallArgs[i]))
|
|
let argsStr = args.join(", ")
|
|
return &"({receiver}.vtable->{methodName}({argsStr}))"
|
|
|
|
of hIf:
|
|
# Ternary expression
|
|
let cond = be.emitExpr(node.ifCond)
|
|
let thenE = be.emitExpr(node.ifThen)
|
|
let elseE = be.emitExpr(node.ifElse)
|
|
return &"({cond} ? {thenE} : {elseE})"
|
|
|
|
of hAssign:
|
|
let target = be.emitExpr(node.assignTarget)
|
|
let value = be.emitExpr(node.assignValue)
|
|
let op = operatorToC(node.assignOp)
|
|
return &"({target} {op} {value})"
|
|
|
|
of hBlock:
|
|
# For block expressions, just emit the last expression
|
|
if node.blockExpr != nil:
|
|
return be.emitExpr(node.blockExpr)
|
|
elif node.blockStmts.len > 0:
|
|
return be.emitExpr(node.blockStmts[^1])
|
|
return "0"
|
|
|
|
of hMatch:
|
|
return "0" # TODO: match expression lowering
|
|
|
|
else:
|
|
return "0"
|
|
|
|
proc emitStmt(be: var CBackend, node: HirNode) =
|
|
if node == nil: return
|
|
case node.kind
|
|
of hReturn:
|
|
if node.returnValue != nil:
|
|
let val = be.emitExpr(node.returnValue)
|
|
be.emitLine(&"return {val};")
|
|
else:
|
|
be.emitLine("return;")
|
|
|
|
of hIf:
|
|
let cond = be.emitExpr(node.ifCond)
|
|
be.emitLine(&"if ({cond}) {{")
|
|
inc be.indent
|
|
be.emitStmt(node.ifThen)
|
|
dec be.indent
|
|
if node.ifElse != nil:
|
|
be.emitLine("} else {")
|
|
inc be.indent
|
|
be.emitStmt(node.ifElse)
|
|
dec be.indent
|
|
be.emitLine("}")
|
|
|
|
of hWhile:
|
|
let cond = be.emitExpr(node.whileCond)
|
|
be.emitLine(&"while ({cond}) {{")
|
|
inc be.indent
|
|
be.emitStmt(node.whileBody)
|
|
dec be.indent
|
|
be.emitLine("}")
|
|
|
|
of hLoop:
|
|
be.emitLine("while (1) {")
|
|
inc be.indent
|
|
be.emitStmt(node.loopBody)
|
|
dec be.indent
|
|
be.emitLine("}")
|
|
|
|
of hBreak:
|
|
be.emitLine("break;")
|
|
|
|
of hContinue:
|
|
be.emitLine("continue;")
|
|
|
|
of hEmit:
|
|
be.emitLine(node.emitCode)
|
|
|
|
of hBlock:
|
|
if node.isScope:
|
|
be.emitLine("{")
|
|
inc be.indent
|
|
for stmt in node.blockStmts:
|
|
be.emitStmt(stmt)
|
|
if node.blockExpr != nil:
|
|
let val = be.emitExpr(node.blockExpr)
|
|
be.emitLine(&"{val};")
|
|
if node.isScope:
|
|
dec be.indent
|
|
be.emitLine("}")
|
|
|
|
of hAlloca:
|
|
let typ = typeToC(be, node.allocaType)
|
|
be.emitLine(&"{typ} {node.allocaName};")
|
|
|
|
of hStore:
|
|
let ptrExpr = be.emitExpr(node.storePtr)
|
|
let val = be.emitExpr(node.storeValue)
|
|
be.emitLine(&"{ptrExpr} = {val};")
|
|
|
|
of hAssign:
|
|
let target = be.emitExpr(node.assignTarget)
|
|
let value = be.emitExpr(node.assignValue)
|
|
let op = operatorToC(node.assignOp)
|
|
be.emitLine(&"{target} {op} {value};")
|
|
|
|
of hCall:
|
|
let expr = be.emitExpr(node)
|
|
be.emitLine(&"{expr};")
|
|
|
|
of hCallIndirect:
|
|
let expr = be.emitExpr(node)
|
|
be.emitLine(&"{expr};")
|
|
|
|
else:
|
|
# Expression statement
|
|
let expr = be.emitExpr(node)
|
|
be.emitLine(&"{expr};")
|
|
|
|
proc emitFunc*(be: var CBackend, hfunc: HirFunc) =
|
|
let retType = typeToC(be, hfunc.retType)
|
|
var params: seq[string] = @[]
|
|
for p in hfunc.params:
|
|
params.add(&"{typeToC(be, p.typ)} {p.name}")
|
|
if params.len == 0:
|
|
params.add("void")
|
|
let paramsStr = params.join(", ")
|
|
be.emitLine(&"{retType} {hfunc.name}({paramsStr}) {{")
|
|
inc be.indent
|
|
if hfunc.body != nil:
|
|
if hfunc.body.kind == hBlock:
|
|
for stmt in hfunc.body.blockStmts:
|
|
be.emitStmt(stmt)
|
|
if hfunc.body.blockExpr != nil and hfunc.retType.kind != tkVoid:
|
|
let val = be.emitExpr(hfunc.body.blockExpr)
|
|
be.emitLine(&"return {val};")
|
|
else:
|
|
be.emitStmt(hfunc.body)
|
|
dec be.indent
|
|
be.emitLine("}")
|
|
be.emitLine("")
|
|
|
|
proc emitStruct*(be: var CBackend, name: string, fields: seq[tuple[name: string, typ: Type]]) =
|
|
be.emitLine(&"typedef struct {name} {{")
|
|
inc be.indent
|
|
for f in fields:
|
|
let typ = typeToC(be, f.typ)
|
|
be.emitLine(&"{typ} {f.name};")
|
|
dec be.indent
|
|
be.emitLine(&"}} {name};")
|
|
be.emitLine("")
|
|
|
|
proc emitEnum*(be: var CBackend, name: string, variants: seq[HirEnumVariant]) =
|
|
# Check if this is a simple enum (no data) or algebraic enum (with data)
|
|
var hasData = false
|
|
for v in variants:
|
|
if v.fields.len > 0 or v.namedFields.len > 0:
|
|
hasData = true
|
|
break
|
|
|
|
if not hasData:
|
|
# Simple enum - generate as before
|
|
be.emitLine(&"typedef enum {{")
|
|
inc be.indent
|
|
for i, v in variants:
|
|
if i < variants.len - 1:
|
|
be.emitLine(&"{name}_{v.name},")
|
|
else:
|
|
be.emitLine(&"{name}_{v.name}")
|
|
dec be.indent
|
|
be.emitLine(&"}} {name};")
|
|
be.emitLine("")
|
|
else:
|
|
# Algebraic enum - generate tagged union
|
|
# 1. Generate tag enum
|
|
be.emitLine(&"typedef enum {{")
|
|
inc be.indent
|
|
for i, v in variants:
|
|
if i < variants.len - 1:
|
|
be.emitLine(&"{name}_{v.name},")
|
|
else:
|
|
be.emitLine(&"{name}_{v.name}")
|
|
dec be.indent
|
|
be.emitLine(&"}} {name}_Tag;")
|
|
be.emitLine("")
|
|
|
|
# 2. Generate union for data
|
|
be.emitLine(&"typedef union {{")
|
|
inc be.indent
|
|
for v in variants:
|
|
if v.fields.len > 0:
|
|
# Positional fields
|
|
for i, f in v.fields:
|
|
let typ = typeToC(be, f)
|
|
be.emitLine(&"{typ} {v.name}_{i};")
|
|
elif v.namedFields.len > 0:
|
|
# Named fields - generate as struct
|
|
be.emitLine(&"struct {{")
|
|
inc be.indent
|
|
for nf in v.namedFields:
|
|
let typ = typeToC(be, nf.typ)
|
|
be.emitLine(&"{typ} {nf.name};")
|
|
dec be.indent
|
|
be.emitLine(&"}} {v.name};")
|
|
dec be.indent
|
|
be.emitLine(&"}} {name}_Data;")
|
|
be.emitLine("")
|
|
|
|
# 3. Generate main struct with tag + union
|
|
be.emitLine(&"typedef struct {{")
|
|
inc be.indent
|
|
be.emitLine(&"{name}_Tag tag;")
|
|
be.emitLine(&"{name}_Data data;")
|
|
dec be.indent
|
|
be.emitLine(&"}} {name};")
|
|
be.emitLine("")
|
|
|
|
proc emitExternDecl*(be: var CBackend, efunc: HirFunc) =
|
|
let retType = typeToC(be, efunc.retType)
|
|
var params: seq[string] = @[]
|
|
for p in efunc.params:
|
|
params.add(&"{typeToC(be, p.typ)} {p.name}")
|
|
if params.len == 0:
|
|
params.add("void")
|
|
let paramsStr = params.join(", ")
|
|
be.emitLine(&"extern {retType} {efunc.name}({paramsStr});")
|
|
|
|
proc collectSliceTypes(module: HirModule): seq[tuple[name: string, elem: string]] =
|
|
## Pre-pass: collect all slice types used in the module.
|
|
var dummyBe = initCBackend()
|
|
for f in module.funcs:
|
|
discard typeToC(dummyBe, f.retType)
|
|
for p in f.params:
|
|
discard typeToC(dummyBe, p.typ)
|
|
for ef in module.externFuncs:
|
|
discard typeToC(dummyBe, ef.retType)
|
|
for p in ef.params:
|
|
discard typeToC(dummyBe, p.typ)
|
|
for s in module.structs:
|
|
for f in s.fields:
|
|
discard typeToC(dummyBe, f.typ)
|
|
for c in module.consts:
|
|
if c.value != nil:
|
|
discard typeToC(dummyBe, c.value.typ)
|
|
return dummyBe.sliceTypeDefs
|
|
|
|
proc emitModule*(be: var CBackend, module: HirModule): string =
|
|
# Header
|
|
be.emitLine("/* Generated by Bux Compiler */")
|
|
be.emitLine("#include <stdio.h>")
|
|
be.emitLine("#include <stdlib.h>")
|
|
be.emitLine("#include <stdint.h>")
|
|
be.emitLine("#include <stdbool.h>")
|
|
be.emitLine("#include <string.h>")
|
|
be.emitLine("")
|
|
|
|
# Pre-collect slice types so we can emit forward declarations early
|
|
let sliceTypes = collectSliceTypes(module)
|
|
|
|
# Forward declarations
|
|
for s in module.structs:
|
|
be.emitLine(&"typedef struct {s.name} {s.name};")
|
|
if module.structs.len > 0:
|
|
be.emitLine("")
|
|
# Forward declarations for trait object fat pointers
|
|
for iface in module.interfaces:
|
|
if not iface.hasAssocTypes:
|
|
be.emitLine(&"typedef struct {iface.name}_FatPtr {iface.name}_FatPtr;")
|
|
if module.interfaces.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("")
|
|
|
|
# Const declarations as #define
|
|
if module.consts.len > 0:
|
|
be.emitLine("/* Constants */")
|
|
for c in module.consts:
|
|
let val = c.value
|
|
if val != nil and val.kind == hLit:
|
|
let tok = val.litToken
|
|
case tok.kind
|
|
of tkIntLiteral:
|
|
be.emitLine(&"#define {c.name} {tok.text}")
|
|
of tkStringLiteral:
|
|
be.emitLine(&"#define {c.name} \"{cEscape(tok.text)}\"")
|
|
of tkBoolLiteral:
|
|
be.emitLine(&"#define {c.name} {tok.text}")
|
|
else:
|
|
be.emitLine(&"/* const {c.name} (unsupported literal kind) */")
|
|
else:
|
|
be.emitLine(&"/* const {c.name} (complex expression) */")
|
|
be.emitLine("")
|
|
|
|
# Forward declarations for all structs
|
|
for s in module.structs:
|
|
be.emitLine(&"typedef struct {s.name} {s.name};")
|
|
if module.structs.len > 0:
|
|
be.emitLine("")
|
|
|
|
# Enum definitions (must come before structs that reference them)
|
|
for e in module.enums:
|
|
be.emitEnum(e.name, e.variants)
|
|
if module.enums.len > 0:
|
|
be.emitLine("")
|
|
|
|
# Struct definitions
|
|
for s in module.structs:
|
|
be.emitStruct(s.name, s.fields)
|
|
|
|
# Slice fat-pointer typedefs
|
|
if sliceTypes.len > 0:
|
|
be.emitLine("/* Slice types */")
|
|
for st in sliceTypes:
|
|
be.emitLine(&"typedef struct {{ {st.elem}* data; size_t len; }} {st.name};")
|
|
be.emitLine("")
|
|
|
|
# Forward declarations for all functions
|
|
for f in module.funcs:
|
|
let retType = typeToC(be, f.retType)
|
|
var params: seq[string] = @[]
|
|
for p in f.params:
|
|
params.add(typeToC(be, p.typ) & " " & p.name)
|
|
if params.len == 0:
|
|
params.add("void")
|
|
be.emitLine(retType & " " & f.name & "(" & params.join(", ") & ");")
|
|
be.emitLine("")
|
|
|
|
# Trait object vtable and fat pointer struct definitions
|
|
for iface in module.interfaces:
|
|
if iface.hasAssocTypes:
|
|
continue # Skip vtables for interfaces with associated types (not yet supported)
|
|
let ifaceName = iface.name
|
|
# VTable struct
|
|
be.emitLine(&"typedef struct {ifaceName}_VTable {{")
|
|
inc be.indent
|
|
for m in iface.methods:
|
|
var paramCtypes: seq[string] = @[]
|
|
for i, p in m.params:
|
|
if i == 0:
|
|
paramCtypes.add("void* self") # First param is always self (erased)
|
|
else:
|
|
paramCtypes.add(typeToC(be, p) & " param")
|
|
if paramCtypes.len == 0:
|
|
paramCtypes.add("void")
|
|
let ret = typeToC(be, m.ret)
|
|
let paramsStr = paramCtypes.join(", ")
|
|
be.emitLine(&"{ret} (*{m.name})({paramsStr});")
|
|
dec be.indent
|
|
be.emitLine(&"}} {ifaceName}_VTable;")
|
|
# Fat pointer struct
|
|
be.emitLine(&"typedef struct {ifaceName}_FatPtr {{")
|
|
inc be.indent
|
|
be.emitLine("void* data;")
|
|
be.emitLine(&"{ifaceName}_VTable* vtable;")
|
|
dec be.indent
|
|
be.emitLine(&"}} {ifaceName}_FatPtr;")
|
|
be.emitLine("")
|
|
|
|
# VTable instances
|
|
for vt in module.vtables:
|
|
if vt.hasAssocTypes:
|
|
continue # Skip vtables for interfaces with associated types
|
|
let varName = vt.concreteType & "_" & vt.interfaceName & "_VTable"
|
|
be.emitLine(&"{vt.interfaceName}_VTable {varName} = {{")
|
|
inc be.indent
|
|
for m in vt.methodNames:
|
|
be.emitLine(&".{m} = (void*){vt.concreteType}_{m},")
|
|
dec be.indent
|
|
be.emitLine("};")
|
|
be.emitLine("")
|
|
|
|
# Function definitions
|
|
var hasMain = false
|
|
for f in module.funcs:
|
|
be.emitFunc(f)
|
|
if f.name == "Main":
|
|
hasMain = true
|
|
|
|
# Generate C main wrapper if Bux Main exists
|
|
if hasMain:
|
|
be.emitLine("/* C entry point wrapper */")
|
|
be.emitLine("extern int g_argc;")
|
|
be.emitLine("extern char** g_argv;")
|
|
be.emitLine("int main(int argc, char** argv) {")
|
|
be.emitLine(" g_argc = argc;")
|
|
be.emitLine(" g_argv = argv;")
|
|
be.emitLine(" return Main();")
|
|
be.emitLine("}")
|
|
be.emitLine("")
|
|
|
|
return be.output
|