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:
@@ -238,6 +238,7 @@ type
|
||||
skStaticAssert
|
||||
skComptime
|
||||
skEmit
|
||||
skDefer
|
||||
skDecl
|
||||
|
||||
ElseIf* = object
|
||||
@@ -298,6 +299,8 @@ type
|
||||
of skEmit:
|
||||
stmtEmitExpr*: Expr
|
||||
stmtEmitEvaluated*: string ## filled by sema CTFE
|
||||
of skDefer:
|
||||
stmtDeferBody*: Expr
|
||||
of skDecl:
|
||||
stmtDecl*: Decl
|
||||
|
||||
|
||||
@@ -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("")
|
||||
|
||||
@@ -17,6 +17,7 @@ type
|
||||
hBreak
|
||||
hContinue
|
||||
hReturn
|
||||
hDefer
|
||||
# Memory
|
||||
hAlloca
|
||||
hLoad
|
||||
@@ -84,6 +85,8 @@ type
|
||||
continueLabel*: string
|
||||
of hReturn:
|
||||
returnValue*: HirNode
|
||||
of hDefer:
|
||||
deferBody*: HirNode
|
||||
of hAlloca:
|
||||
allocaType*: Type
|
||||
allocaName*: string
|
||||
@@ -200,6 +203,9 @@ proc hirCall*(callee: string, args: seq[HirNode], typ: Type, loc: SourceLocation
|
||||
proc hirReturn*(value: HirNode, loc: SourceLocation): HirNode =
|
||||
HirNode(kind: hReturn, returnValue: value, typ: makeVoid(), loc: loc)
|
||||
|
||||
proc hirDefer*(body: HirNode, loc: SourceLocation): HirNode =
|
||||
HirNode(kind: hDefer, deferBody: body, typ: makeVoid(), loc: loc)
|
||||
|
||||
proc hirBlock*(stmts: seq[HirNode], expr: HirNode, typ: Type, loc: SourceLocation, isScope: bool = false): HirNode =
|
||||
HirNode(kind: hBlock, blockStmts: stmts, blockExpr: expr, typ: typ, loc: loc, isScope: isScope)
|
||||
|
||||
|
||||
+31
-2
@@ -11,6 +11,7 @@ type
|
||||
varCounter*: int
|
||||
tryCounter*: int
|
||||
pendingStmts*: seq[HirNode]
|
||||
deferStmts*: seq[HirNode]
|
||||
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
||||
importTable*: Table[string, string] # Local name → fully qualified name for imports
|
||||
genericStructs*: Table[string, Decl] # Generic struct declarations
|
||||
@@ -1005,7 +1006,13 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
||||
|
||||
of skReturn:
|
||||
let value = if stmt.stmtReturnValue != nil: ctx.lowerExpr(stmt.stmtReturnValue) else: nil
|
||||
return ctx.flushPending(hirReturn(value, loc))
|
||||
var stmts = ctx.pendingStmts
|
||||
ctx.pendingStmts = @[]
|
||||
# Add defers in reverse order (LIFO)
|
||||
for i in countdown(ctx.deferStmts.len - 1, 0):
|
||||
stmts.add(ctx.deferStmts[i])
|
||||
stmts.add(hirReturn(value, loc))
|
||||
return hirBlock(stmts, nil, makeVoid(), loc)
|
||||
|
||||
of skIf:
|
||||
let cond = ctx.lowerExpr(stmt.stmtIfCond)
|
||||
@@ -1120,6 +1127,11 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
||||
return ctx.flushPending(HirNode(kind: hMatch, matchSubject: subject, matchArms: arms,
|
||||
typ: makeVoid(), loc: loc))
|
||||
|
||||
of skDefer:
|
||||
let body = ctx.lowerExpr(stmt.stmtDeferBody)
|
||||
ctx.deferStmts.add(body)
|
||||
return nil
|
||||
|
||||
of skDecl:
|
||||
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
||||
typ: makeVoid(), loc: loc)
|
||||
@@ -1188,7 +1200,24 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
ctx.currentFuncRetType = retType
|
||||
ctx.currentFuncDecl = decl
|
||||
ctx.varTypeExprs = initTable[string, TypeExpr]() # Clear local vars for new function
|
||||
let body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
|
||||
var body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
|
||||
|
||||
# Inject remaining defers at end of function (for implicit return)
|
||||
if ctx.deferStmts.len > 0 and body != nil and body.kind == hBlock:
|
||||
# Only add if last statement is not already a return (defers already injected there)
|
||||
var hasReturn = false
|
||||
if body.blockStmts.len > 0 and body.blockStmts[^1].kind == hReturn:
|
||||
hasReturn = true
|
||||
elif body.blockStmts.len > 0 and body.blockStmts[^1].kind == hBlock:
|
||||
# Check nested block's last statement
|
||||
let last = body.blockStmts[^1]
|
||||
if last.blockStmts.len > 0 and last.blockStmts[^1].kind == hReturn:
|
||||
hasReturn = true
|
||||
if not hasReturn:
|
||||
for i in countdown(ctx.deferStmts.len - 1, 0):
|
||||
body.blockStmts.add(ctx.deferStmts[i])
|
||||
ctx.deferStmts = @[]
|
||||
|
||||
ctx.currentFuncDecl = oldFuncDecl
|
||||
ctx.currentFuncRetType = oldFuncRetType
|
||||
ctx.varTypeExprs = oldVarTypeExprs
|
||||
|
||||
@@ -959,6 +959,12 @@ proc parseStmt(p: var Parser): Stmt =
|
||||
# No-op: emit literal 0 as expression statement
|
||||
let zeroTok = Token(kind: tkIntLiteral, text: "0", loc: loc)
|
||||
return Stmt(kind: skExpr, loc: loc, stmtExpr: Expr(kind: ekLiteral, loc: loc, exprLit: zeroTok))
|
||||
of tkDefer:
|
||||
discard p.advance()
|
||||
let expr = p.parseExpr()
|
||||
if p.check(tkSemicolon):
|
||||
discard p.advance()
|
||||
return Stmt(kind: skDefer, loc: loc, stmtDeferBody: expr)
|
||||
of tkFunc, tkStruct, tkEnum, tkUnion, tkInterface, tkExtend, tkModule,
|
||||
tkImport, tkConst, tkType, tkExtern, tkPub:
|
||||
# Local declaration
|
||||
|
||||
@@ -1335,6 +1335,9 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
|
||||
elif not exprType.isUnknown and exprType.kind != tkStr:
|
||||
sema.emitError(stmt.loc, "#emit requires a string expression")
|
||||
return makeVoid()
|
||||
of skDefer:
|
||||
discard sema.checkExpr(stmt.stmtDeferBody, scope)
|
||||
return makeVoid()
|
||||
of skDecl:
|
||||
# Local declaration inside block
|
||||
case stmt.stmtDecl.kind
|
||||
|
||||
+4
-1
@@ -59,6 +59,7 @@ type
|
||||
tkStaticAssert # static_assert
|
||||
tkComptime # comptime
|
||||
tkDyn # dyn
|
||||
tkDefer # defer
|
||||
tkLifetime # 'a (lifetime parameter)
|
||||
|
||||
##Punctuation
|
||||
@@ -152,7 +153,7 @@ proc isKeyword*(kind: TokenKind): bool =
|
||||
tkBreak, tkContinue, tkReturn, tkMatch,
|
||||
tkFunc, tkLet, tkVar, tkConst, tkType, tkStruct, tkEnum,
|
||||
tkUnion, tkInterface, tkExtend, tkModule, tkImport,
|
||||
tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper, tkOwn, tkMut, tkBorrow, tkDiscard, tkAsync, tkAwait, tkSpawn, tkStaticAssert, tkComptime, tkDyn:
|
||||
tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper, tkOwn, tkMut, tkBorrow, tkDiscard, tkAsync, tkAwait, tkSpawn, tkStaticAssert, tkComptime, tkDyn, tkDefer:
|
||||
true
|
||||
else:
|
||||
false
|
||||
@@ -216,6 +217,7 @@ proc keywordKind*(text: string): TokenKind =
|
||||
of "static_assert": tkStaticAssert
|
||||
of "comptime": tkComptime
|
||||
of "dyn": tkDyn
|
||||
of "defer": tkDefer
|
||||
of "true", "false": tkBoolLiteral
|
||||
else: tkIdent
|
||||
|
||||
@@ -269,6 +271,7 @@ proc tokenKindName*(kind: TokenKind): string =
|
||||
of tkStaticAssert: "'static_assert'"
|
||||
of tkComptime: "'comptime'"
|
||||
of tkDyn: "'dyn'"
|
||||
of tkDefer: "'defer'"
|
||||
of tkLifetime: "lifetime"
|
||||
of tkLParen: "'('"
|
||||
of tkRParen: "')'"
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
# Bux Language Roadmap — New Constructs
|
||||
|
||||
> **Updated:** 2026-06-08 | **Status:** In Progress
|
||||
|
||||
This document tracks planned language constructs beyond Phase 8 strategy.
|
||||
|
||||
---
|
||||
|
||||
## P0 — Critical (Unlocks Major Use Cases)
|
||||
|
||||
### 1. `defer` Statement
|
||||
**Why:** No GC + no destructors = manual `Free` everywhere. `defer` is the pragmatic fix.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
func ReadFile(path: String) -> String {
|
||||
let fd: int = Open(path);
|
||||
defer Close(fd); // runs on any exit from scope
|
||||
defer PrintLine("done"); // LIFO order
|
||||
let data: String = ReadAll(fd);
|
||||
return data; // both defers run before return
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Add `tkDefer` token (or reuse `tkDefer` if exists)
|
||||
2. Add `DeferStmt` AST node (`child: *Stmt`)
|
||||
3. Parser: parse `defer <expr>;` or `defer { <block> }`
|
||||
4. C backend: collect all `defer`s per block, emit cleanup code before every exit point (return, break, continue)
|
||||
5. Handle LIFO ordering for multiple defers in same scope
|
||||
|
||||
**Complexity:** Low — localized to parser + C backend. No type system changes.
|
||||
|
||||
---
|
||||
|
||||
### 2. Closures / Anonymous Functions
|
||||
**Why:** Callbacks, iterators, functional APIs. Currently only named functions exist.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
let add: func(int, int) -> int = |a, b| { return a + b; };
|
||||
let nums: Array<int> = Array_New<int>();
|
||||
Array_Filter(nums, |x| { return x > 10; });
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. New AST node: `ClosureExpr` with `params`, `body`, `captures`
|
||||
2. Parser: parse `|param1, param2| -> Type { body }`
|
||||
3. Type system: closure type as `func(Args) -> Ret` + implicit capture struct
|
||||
4. C backend: generate struct with captured vars + function pointer
|
||||
5. Lifetime: ensure captures outlive closure usage
|
||||
|
||||
**Complexity:** High — touches parser, sema, type system, C backend.
|
||||
|
||||
---
|
||||
|
||||
### 3. `for x in collection` Iterator Loops
|
||||
**Why:** Currently only `for i in 0..10` works. No way to iterate arrays/channels/maps.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
for item in arr {
|
||||
PrintLine(item);
|
||||
}
|
||||
for msg in channel {
|
||||
Process(msg);
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Parser: extend `for` to accept `for <ident> in <expr> { ... }`
|
||||
2. Desugar to while loop using `Iter_HasNext` / `Iter_Next` or trait-based iterator
|
||||
3. C backend: generate standard while loop with iterator state
|
||||
|
||||
**Complexity:** Medium — needs either trait system enhancement or hardcoded desugaring.
|
||||
|
||||
---
|
||||
|
||||
## P1 — High Impact
|
||||
|
||||
### 4. Operator Overloading
|
||||
**Why:** Can't write `arr[i]`, `a + b`, `s1 == s2` for user types.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
extend Array<T> {
|
||||
func operator[](self: Array<T>, idx: uint) -> T { ... }
|
||||
func operator+(self: Array<T>, other: Array<T>) -> Array<T> { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Parser: allow `operator[]`, `operator+`, etc. as function names
|
||||
2. Sema: resolve operator calls to user-defined functions when available
|
||||
3. C backend: emit regular function call
|
||||
|
||||
**Complexity:** Medium — mainly sema + parser changes.
|
||||
|
||||
---
|
||||
|
||||
### 5. Destructors / `Drop` Trait
|
||||
**Why:** `own T` exists but nothing cleans up automatically. Complements `defer`.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
extend Array<T> {
|
||||
func Drop(self: own Array<T>) {
|
||||
Array_Free(self);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Define `Drop` interface in stdlib
|
||||
2. C backend: emit `Drop(value)` before variable goes out of scope
|
||||
3. Respect move semantics — don't drop moved values
|
||||
|
||||
**Complexity:** High — needs ownership tracking + move semantics.
|
||||
|
||||
---
|
||||
|
||||
### 6. String Interpolation
|
||||
**Why:** `Fmt_Fmt1("hello {0}", name)` is verbose.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
let name: String = "Bux";
|
||||
let msg: String = "Hello, {name}!";
|
||||
let num: int = 42;
|
||||
let msg2: String = "Count: {num}";
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Lexer: detect `{` inside string literals, parse interpolation expressions
|
||||
2. Parser: create string concatenation AST node
|
||||
3. Desugar to `String_Concat` calls or `Fmt_FmtN`
|
||||
|
||||
**Complexity:** Low — lexer/parser changes only.
|
||||
|
||||
---
|
||||
|
||||
## P2 — Nice to Have
|
||||
|
||||
### 7. Native `switch` / `case`
|
||||
**Why:** `match` is powerful but overkill for simple integer dispatch. Jump tables are faster.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
switch statusCode {
|
||||
case 200: PrintLine("OK");
|
||||
case 404: PrintLine("Not Found");
|
||||
case 500: PrintLine("Server Error");
|
||||
default: PrintLine("Unknown");
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Parser: `switch expr { case literal: stmts ... default: stmts }`
|
||||
2. C backend: emit `switch(expr) { case N: ... }`
|
||||
|
||||
**Complexity:** Low — straightforward C mapping.
|
||||
|
||||
---
|
||||
|
||||
### 8. Named / Default Parameters
|
||||
**Why:** API ergonomics.
|
||||
|
||||
**Syntax:**
|
||||
```bux
|
||||
func HttpResponse(code: int = 200, contentType: String = "text/plain", body: String = "") -> Response { ... }
|
||||
let r: Response = HttpResponse(body: "hello"); // code=200, contentType=default
|
||||
```
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Parser: allow `param: Type = defaultExpr`
|
||||
2. Sema: fill missing args at call sites
|
||||
3. C backend: emit args in correct order with defaults
|
||||
|
||||
**Complexity:** Medium — sema changes for call resolution.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Order
|
||||
|
||||
1. **`defer`** — Low complexity, huge impact, unlocks safe resource management immediately.
|
||||
2. **String interpolation** — Low complexity, big ergonomics win.
|
||||
3. **`switch`/`case`** — Low complexity, complements `match` for numeric dispatch.
|
||||
4. **Named/default parameters** — Medium complexity, improves stdlib APIs.
|
||||
5. **Operator overloading** — Medium complexity, transforms stdlib ergonomics.
|
||||
6. **Closures** — High complexity, unlocks iterators and functional style.
|
||||
7. **`for x in collection`** — Depends on closures or trait system.
|
||||
8. **Destructors / Drop** — High complexity, needs ownership + move semantics.
|
||||
@@ -157,6 +157,7 @@ const skReturn: int = 8;
|
||||
const skBreak: int = 9;
|
||||
const skContinue: int = 10;
|
||||
const skDecl: int = 11;
|
||||
const skDefer: int = 12;
|
||||
|
||||
struct ElseIf {
|
||||
line: uint32;
|
||||
|
||||
+74
-1
@@ -66,6 +66,15 @@ struct CEmitter {
|
||||
sb: StringBuilder,
|
||||
indent: int,
|
||||
mod: *HirModule,
|
||||
deferCount: int,
|
||||
defer0: *HirNode,
|
||||
defer1: *HirNode,
|
||||
defer2: *HirNode,
|
||||
defer3: *HirNode,
|
||||
defer4: *HirNode,
|
||||
defer5: *HirNode,
|
||||
defer6: *HirNode,
|
||||
defer7: *HirNode,
|
||||
}
|
||||
|
||||
func CBE_Init() -> CEmitter {
|
||||
@@ -73,6 +82,49 @@ func CBE_Init() -> CEmitter {
|
||||
return CEmitter { sb: sb, indent: 0, mod: null as *HirModule };
|
||||
}
|
||||
|
||||
func CBE_PushDefer(cbe: *CEmitter, node: *HirNode) {
|
||||
if cbe.deferCount == 0 { cbe.defer0 = node; }
|
||||
if cbe.deferCount == 1 { cbe.defer1 = node; }
|
||||
if cbe.deferCount == 2 { cbe.defer2 = node; }
|
||||
if cbe.deferCount == 3 { cbe.defer3 = node; }
|
||||
if cbe.deferCount == 4 { cbe.defer4 = node; }
|
||||
if cbe.deferCount == 5 { cbe.defer5 = node; }
|
||||
if cbe.deferCount == 6 { cbe.defer6 = node; }
|
||||
if cbe.deferCount == 7 { cbe.defer7 = node; }
|
||||
cbe.deferCount = cbe.deferCount + 1;
|
||||
}
|
||||
|
||||
func CBE_EmitDefers(cbe: *CEmitter) -> int {
|
||||
if cbe.deferCount == 0 { return 0; }
|
||||
var i: int = cbe.deferCount - 1;
|
||||
while i >= 0 {
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
var dn: *HirNode = null as *HirNode;
|
||||
if i == 0 { dn = cbe.defer0; }
|
||||
if i == 1 { dn = cbe.defer1; }
|
||||
if i == 2 { dn = cbe.defer2; }
|
||||
if i == 3 { dn = cbe.defer3; }
|
||||
if i == 4 { dn = cbe.defer4; }
|
||||
if i == 5 { dn = cbe.defer5; }
|
||||
if i == 6 { dn = cbe.defer6; }
|
||||
if i == 7 { dn = cbe.defer7; }
|
||||
CBE_EmitExpr(cbe, dn);
|
||||
StringBuilder_Append(&cbe.sb, ";");
|
||||
i = i - 1;
|
||||
}
|
||||
cbe.deferCount = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
func CBE_ClearDefers(cbe: *CEmitter) {
|
||||
cbe.deferCount = 0;
|
||||
}
|
||||
|
||||
func CBE_Emit(cbe: *CEmitter, text: String) {
|
||||
var i: int = 0;
|
||||
while i < cbe.indent {
|
||||
@@ -231,8 +283,23 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer
|
||||
if kind == hDefer {
|
||||
CBE_PushDefer(cbe, node.child1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Return
|
||||
if kind == hReturn {
|
||||
let hadDefers: int = CBE_EmitDefers(cbe);
|
||||
if hadDefers != 0 {
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
sp = sp + 1;
|
||||
}
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "return");
|
||||
if node.child1 != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
@@ -500,6 +567,11 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
if kind == hBlock {
|
||||
var child: *HirNode = node.child1;
|
||||
while child != null as *HirNode {
|
||||
if child.kind == hDefer {
|
||||
CBE_EmitExpr(cbe, child);
|
||||
child = child.child3;
|
||||
continue;
|
||||
}
|
||||
// Indent
|
||||
var sp: int = 0;
|
||||
while sp < cbe.indent {
|
||||
@@ -971,6 +1043,7 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
var hasReturn: bool = false;
|
||||
cbe.indent = 1;
|
||||
CBE_EmitExpr(cbe, body);
|
||||
CBE_EmitDefers(cbe);
|
||||
cbe.indent = 0;
|
||||
// Check if body has a return statement
|
||||
var stmt: *HirNode = body.child1;
|
||||
@@ -982,7 +1055,7 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
if String_Eq(mod.funcs[i].retTypeName, "int") && !hasReturn {
|
||||
StringBuilder_Append(&cbe.sb, " return 0;\n");
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "}\n\n");
|
||||
StringBuilder_Append(&cbe.sb, "\n}\n\n");
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ const hTupleInit: int = 27;
|
||||
const hMatch: int = 28;
|
||||
const hSpawn: int = 29;
|
||||
const hAwait: int = 30;
|
||||
const hDefer: int = 31;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HirArgList — linked list for call arguments beyond 2
|
||||
|
||||
@@ -482,6 +482,15 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Defer
|
||||
if kind == skDefer {
|
||||
n.kind = hDefer;
|
||||
if stmt.child1 != null as *Expr {
|
||||
n.child1 = Lcx_LowerExpr(ctx, stmt.child1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ func lexKeywordKind(text: String) -> int {
|
||||
if String_Eq(text, "super") { return tkSuper; }
|
||||
if String_Eq(text, "sizeof") { return tkSizeOf; }
|
||||
if String_Eq(text, "discard") { return tkDiscard; }
|
||||
if String_Eq(text, "defer") { return tkDefer; }
|
||||
if String_Eq(text, "async") { return tkAsync; }
|
||||
if String_Eq(text, "await") { return tkAwait; }
|
||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||
|
||||
@@ -704,6 +704,19 @@ func parserParseStmt(p: *Parser) -> *Stmt {
|
||||
return s;
|
||||
}
|
||||
|
||||
// defer expr
|
||||
if kind == tkDefer {
|
||||
discard parserAdvance(p);
|
||||
let val: *Expr = parserParseExpr(p);
|
||||
parserMatch(p, tkSemicolon);
|
||||
let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt;
|
||||
s.kind = skDefer;
|
||||
s.line = line;
|
||||
s.column = col;
|
||||
s.child1 = val;
|
||||
return s;
|
||||
}
|
||||
|
||||
// if
|
||||
if kind == tkIf {
|
||||
discard parserAdvance(p);
|
||||
|
||||
@@ -138,6 +138,7 @@ const tkAsync: int = 102;
|
||||
const tkAwait: int = 103;
|
||||
const tkSpawn: int = 104;
|
||||
const tkDiscard: int = 105;
|
||||
const tkDefer: int = 106;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Token struct
|
||||
|
||||
Reference in New Issue
Block a user