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
+39 -4
View File
@@ -323,10 +323,11 @@ proc parsePrimary(p: var Parser): Expr =
case p.peek()
of tkIntLiteral, tkFloatLiteral, tkStringLiteral, tkCharLiteral, tkBoolLiteral:
return newLiteralExpr(p.advance())
of tkSelf:
discard p.advance()
return Expr(kind: ekSelf, loc: loc)
of tkIdent:
let name = p.advance().text
if name == "self":
return Expr(kind: ekSelf, loc: loc)
# Path expression: a::b::c
if p.check(tkColonColon):
var segs = @[name]
@@ -781,11 +782,25 @@ proc parseParamList(p: var Parser, allowVariadic: bool = false): seq[Param] =
var isVar = false
if allowVariadic and p.check(tkDotDotDot):
discard p.advance()
let name = p.expect(tkIdent, "expected parameter name after '...'").text
let nameTok = p.at
var name = ""
if nameTok.kind == tkIdent:
name = p.advance().text
elif nameTok.kind == tkSelf:
name = p.advance().text
else:
name = p.expect(tkIdent, "expected parameter name after '...'").text
let ty = p.parseType()
result.add(Param(loc: loc, name: name, ptype: ty, isVariadic: true))
else:
let name = p.expect(tkIdent, "expected parameter name").text
let nameTok = p.at
var name = ""
if nameTok.kind == tkIdent:
name = p.advance().text
elif nameTok.kind == tkSelf:
name = p.advance().text
else:
name = p.expect(tkIdent, "expected parameter name").text
discard p.expect(tkColon, "expected ':' after parameter name")
let ty = p.parseType()
var defaultVal: Expr = nil
@@ -826,6 +841,11 @@ proc parseStructDecl(p: var Parser, isPublic: bool): Decl =
discard p.expect(tkLBrace, "expected '{' to start struct body")
var fields: seq[StructField] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
# Skip newlines
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
let fLoc = p.currentLoc
var fPub = false
if p.check(tkPub):
@@ -853,6 +873,11 @@ proc parseEnumDecl(p: var Parser, isPublic: bool): Decl =
discard p.expect(tkLBrace, "expected '{' to start enum body")
var variants: seq[EnumVariant] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
# Skip newlines
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
let vLoc = p.currentLoc
let vName = p.expect(tkIdent, "expected variant name").text
var fields: seq[TypeExpr] = @[]
@@ -908,6 +933,11 @@ proc parseInterfaceDecl(p: var Parser, isPublic: bool): Decl =
discard p.expect(tkLBrace, "expected '{' to start interface body")
var methods: seq[Decl] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
# Skip newlines
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
methods.add(p.parseFuncDecl(false, false, ParsedAttrs()))
discard p.expect(tkRBrace, "expected '}' to close interface")
return Decl(kind: dkInterface, loc: loc, isPublic: isPublic,
@@ -924,6 +954,11 @@ proc parseImplDecl(p: var Parser): Decl =
discard p.expect(tkLBrace, "expected '{' to start impl block")
var methods: seq[Decl] = @[]
while not p.check(tkRBrace) and not p.isAtEnd:
# Skip newlines
while p.check(tkNewLine):
discard p.advance()
if p.check(tkRBrace) or p.isAtEnd:
break
methods.add(p.parseFuncDecl(false, false, ParsedAttrs()))
discard p.expect(tkRBrace, "expected '}' to close impl block")
return Decl(kind: dkImpl, loc: loc, declImplTypeName: typeName,