- 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/
5.5 KiB
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:
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:
- Add
tkDefertoken (or reusetkDeferif exists) - Add
DeferStmtAST node (child: *Stmt) - Parser: parse
defer <expr>;ordefer { <block> } - C backend: collect all
defers per block, emit cleanup code before every exit point (return, break, continue) - 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:
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:
- New AST node:
ClosureExprwithparams,body,captures - Parser: parse
|param1, param2| -> Type { body } - Type system: closure type as
func(Args) -> Ret+ implicit capture struct - C backend: generate struct with captured vars + function pointer
- 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:
for item in arr {
PrintLine(item);
}
for msg in channel {
Process(msg);
}
Implementation Steps:
- Parser: extend
forto acceptfor <ident> in <expr> { ... } - Desugar to while loop using
Iter_HasNext/Iter_Nextor trait-based iterator - 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:
extend Array<T> {
func operator[](self: Array<T>, idx: uint) -> T { ... }
func operator+(self: Array<T>, other: Array<T>) -> Array<T> { ... }
}
Implementation Steps:
- Parser: allow
operator[],operator+, etc. as function names - Sema: resolve operator calls to user-defined functions when available
- 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:
extend Array<T> {
func Drop(self: own Array<T>) {
Array_Free(self);
}
}
Implementation Steps:
- Define
Dropinterface in stdlib - C backend: emit
Drop(value)before variable goes out of scope - 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:
let name: String = "Bux";
let msg: String = "Hello, {name}!";
let num: int = 42;
let msg2: String = "Count: {num}";
Implementation Steps:
- Lexer: detect
{inside string literals, parse interpolation expressions - Parser: create string concatenation AST node
- Desugar to
String_Concatcalls orFmt_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:
switch statusCode {
case 200: PrintLine("OK");
case 404: PrintLine("Not Found");
case 500: PrintLine("Server Error");
default: PrintLine("Unknown");
}
Implementation Steps:
- Parser:
switch expr { case literal: stmts ... default: stmts } - C backend: emit
switch(expr) { case N: ... }
Complexity: Low — straightforward C mapping.
8. Named / Default Parameters
Why: API ergonomics.
Syntax:
func HttpResponse(code: int = 200, contentType: String = "text/plain", body: String = "") -> Response { ... }
let r: Response = HttpResponse(body: "hello"); // code=200, contentType=default
Implementation Steps:
- Parser: allow
param: Type = defaultExpr - Sema: fill missing args at call sites
- C backend: emit args in correct order with defaults
Complexity: Medium — sema changes for call resolution.
Recommended Order
defer— Low complexity, huge impact, unlocks safe resource management immediately.- String interpolation — Low complexity, big ergonomics win.
switch/case— Low complexity, complementsmatchfor numeric dispatch.- Named/default parameters — Medium complexity, improves stdlib APIs.
- Operator overloading — Medium complexity, transforms stdlib ergonomics.
- Closures — High complexity, unlocks iterators and functional style.
for x in collection— Depends on closures or trait system.- Destructors / Drop — High complexity, needs ownership + move semantics.