feat: defer statement support in both compilers

- Add  statement for function-level deferred execution
- Deferred expressions run in LIFO order on function exit (return or implicit)
- Bootstrap: desugar defers in hir_lower.nim (inject before return + end of func)
- Selfhost: emit defers in c_backend.bux via CEmitter defer stack
- Both: add tkDefer token, skDefer AST node, hDefer HIR node
- Parser, lexer, sema, token files updated in both bootstrap/ and src/
This commit is contained in:
2026-06-08 20:59:27 +03:00
parent cf92b63ba6
commit a67271b08c
15 changed files with 385 additions and 4 deletions
+40
View File
@@ -8,6 +8,7 @@ type
varCounter*: int
declaredVars*: seq[string]
sliceTypeDefs*: seq[tuple[name: string, elem: string]] ## Generated Slice_<T> typedefs
deferStack*: seq[seq[string]] ## Function-level deferred statements (LIFO)
proc cEscape(s: string): string =
## Escape a string for use as a C string literal.
@@ -45,6 +46,28 @@ proc freshVar(be: var CBackend): string =
inc be.varCounter
result = &"__tmp_{be.varCounter}"
# ---------------------------------------------------------------------------
# Defer helpers
# ---------------------------------------------------------------------------
proc pushDeferScope(be: var CBackend) =
be.deferStack.add(@[])
proc popDeferScope(be: var CBackend) =
if be.deferStack.len > 0:
be.deferStack.setLen(be.deferStack.len - 1)
proc emitCurrentDefers(be: var CBackend) =
## Emit all deferred statements in current scope, LIFO order
if be.deferStack.len == 0: return
let idx = be.deferStack.len - 1
for i in countdown(be.deferStack[idx].len - 1, 0):
be.emitLine(be.deferStack[idx][i])
proc clearCurrentDefers(be: var CBackend) =
if be.deferStack.len > 0:
let idx = be.deferStack.len - 1
be.deferStack[idx].setLen(0)
# Type conversion: Bux Type → C type string
proc typeToC*(be: var CBackend, typ: Type): string =
if typ == nil: return "void"
@@ -358,7 +381,14 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
proc emitStmt(be: var CBackend, node: HirNode) =
if node == nil: return
case node.kind
of hDefer:
let expr = be.emitExpr(node.deferBody)
if be.deferStack.len > 0:
let idx = be.deferStack.len - 1
be.deferStack[idx].add(expr & ";")
of hReturn:
be.emitCurrentDefers()
if node.returnValue != nil:
let val = be.emitExpr(node.returnValue)
be.emitLine(&"return {val};")
@@ -394,9 +424,11 @@ proc emitStmt(be: var CBackend, node: HirNode) =
be.emitLine("}")
of hBreak:
be.emitCurrentDefers()
be.emitLine("break;")
of hContinue:
be.emitCurrentDefers()
be.emitLine("continue;")
of hEmit:
@@ -453,15 +485,23 @@ proc emitFunc*(be: var CBackend, hfunc: HirFunc) =
let paramsStr = params.join(", ")
be.emitLine(&"{retType} {hfunc.name}({paramsStr}) {{")
inc be.indent
be.pushDeferScope()
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:
be.emitCurrentDefers()
let val = be.emitExpr(hfunc.body.blockExpr)
be.emitLine(&"return {val};")
else:
be.emitCurrentDefers()
else:
be.emitStmt(hfunc.body)
be.emitCurrentDefers()
else:
be.emitCurrentDefers()
be.popDeferScope()
dec be.indent
be.emitLine("}")
be.emitLine("")