feat: add HIR, C backend, and end-to-end compilation

- Phase 3: High-Level IR (HIR) with lowering from AST
  - Method call desugaring (obj.method() → Type_method(obj))
  - if/else, while, loop, break, continue lowering
  - struct, enum, function lowering
  - 8 HIR tests passing

- Phase 5A: C backend code generation
  - Type mapping (Bux types → C11 types)
  - Expression and statement emission
  - Struct, enum, function generation
  - C main() wrapper for Bux Main()

- Runtime shim (stdlib/runtime.c)
  - bux_alloc, bux_free, bux_print, bux_panic
  - BuxString, BuxSlice types
  - Bounds checking, division by zero

- Build integration
  - bux build: lex → parse → sema → HIR → C → cc
  - bux run: build + execute
  - bux clean: remove build directory

- Parser fixes
  - Newline handling in struct, enum, extend, interface blocks
  - self keyword as expression and parameter name

- Sema improvements
  - Method resolution (extend blocks)
  - Interface conformance checking
  - collectGlobals made public

- All 70 tests passing (25 lexer + 16 parser + 21 sema + 8 HIR)
- End-to-end: Bux programs compile to native ELF64 binaries
This commit is contained in:
2026-05-30 22:40:34 +03:00
parent 8e637c89e7
commit 8e74215378
15 changed files with 2074 additions and 78 deletions
+104
View File
@@ -0,0 +1,104 @@
import std/[unittest, strformat]
import ../src/[lexer, parser, sema, hir, hir_lower, types, scope]
proc lowerSource(source: string): HirModule =
let lexRes = tokenize(source, "<test>")
check not lexRes.hasErrors
let parseRes = parse(lexRes.tokens, "<test>")
check parseRes.diagnostics.len == 0
# Create Sema and populate global scope + method table
var s = Sema(module: parseRes.module, globalScope: newScope())
s.collectGlobals()
result = lowerModule(parseRes.module, s)
suite "HIR Lowering":
test "simple function":
let src = "func Main() -> int { return 0; }"
let hir = lowerSource(src)
check hir.funcs.len == 1
check hir.funcs[0].name == "Main"
check hir.funcs[0].body != nil
check hir.funcs[0].body.kind == hBlock
echo " PASS: simple function"
test "function with arithmetic":
let src = "func Add(a: int, b: int) -> int { return a + b; }"
let hir = lowerSource(src)
check hir.funcs.len == 1
check hir.funcs[0].params.len == 2
echo " PASS: function with arithmetic"
test "struct lowering":
let src = """
struct Point { x: float64; y: float64; }
func Main() -> int { return 0; }
"""
let hir = lowerSource(src)
check hir.structs.len == 1
check hir.structs[0].name == "Point"
check hir.structs[0].fields.len == 2
echo " PASS: struct lowering"
test "method lowering":
let src = """
struct Point { x: float64; }
extend Point {
func GetX(self: Point) -> float64 { return 0.0; }
}
func Main() -> int { return 0; }
"""
let hir = lowerSource(src)
check hir.funcs.len == 2 # GetX (mangled) + Main
var foundMangled = false
for f in hir.funcs:
if f.name == "Point_GetX":
foundMangled = true
check foundMangled
echo " PASS: method lowering"
test "if statement":
let src = """
func Main() -> int {
if true { return 1; }
return 0;
}
"""
let hir = lowerSource(src)
let body = hir.funcs[0].body
check body.kind == hBlock
check body.blockStmts.len >= 1
echo " PASS: if statement"
test "while loop":
let src = """
func Main() -> int {
while true { break; }
return 0;
}
"""
let hir = lowerSource(src)
let body = hir.funcs[0].body
check body.kind == hBlock
echo " PASS: while loop"
test "let statement":
let src = """
func Main() -> int {
let x: int = 42;
return x;
}
"""
let hir = lowerSource(src)
check hir.funcs.len == 1
echo " PASS: let statement"
test "enum lowering":
let src = """
enum Color { Red, Green, Blue }
func Main() -> int { return 0; }
"""
let hir = lowerSource(src)
check hir.enums.len == 1
check hir.enums[0].name == "Color"
check hir.enums[0].variants.len == 3
echo " PASS: enum lowering"