feat: async/await/spawn + fix else-if lowering bug
Self-hosted compiler (buxc2):
- Add async/await/spawn tokens, parsing, HIR, lowering, C emission
- Fix pointer type emission (*T -> T*) in C backend
- Fix sizeof(Type) parsing with parentheses
- Fix import ::{...} infinite loop guard
- Fix int64 emission by avoiding else-if chain workaround
- Add debug-free cli and hir_lower
Bootstrap compiler (Nim):
- Fix critical else-if lowering bug in hir_lower.nim:
when both elseIfs and else block exist, elseIfs were dropped
causing all else-if chains to collapse to if+else only
Runtime:
- Fix bux_async_await memory corruption (don't free in run)
- Add bux_remove_from_ready for safe cleanup
- Fix forward declaration and deduplicate bux_now_ms
Docs & examples:
- Update async.bux example with proper int64 params
- Update README, LanguageRef, Stdlib, PLAN for async features
- Mark Phase 7.10 bootstrap loop as completed
All 27 examples pass. buxc2 check/build work on all examples.
This commit is contained in:
@@ -93,6 +93,8 @@ const ekTry: int = 20;
|
||||
const ekUnwrap: int = 23;
|
||||
const ekBlock: int = 21;
|
||||
const ekMatch: int = 22;
|
||||
const ekSpawn: int = 24;
|
||||
const ekAwait: int = 25;
|
||||
|
||||
struct Expr {
|
||||
kind: int,
|
||||
@@ -223,6 +225,7 @@ struct Decl {
|
||||
line: uint32,
|
||||
column: uint32,
|
||||
isPublic: bool,
|
||||
isAsync: bool,
|
||||
// Names
|
||||
strValue: String, // decl name
|
||||
strValue2: String, // interface name, dll name, module path
|
||||
|
||||
+21
-16
@@ -145,6 +145,26 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// spawn Callee(args)
|
||||
if kind == hSpawn {
|
||||
StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
|
||||
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||
if node.child1 != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, ", (void*)");
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// await
|
||||
if kind == hAwait {
|
||||
StringBuilder_Append(&cbe.sb, "bux_async_await(");
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
StringBuilder_Append(&cbe.sb, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Return
|
||||
if kind == hReturn {
|
||||
StringBuilder_Append(&cbe.sb, "return");
|
||||
@@ -371,24 +391,9 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
||||
// Return type
|
||||
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
||||
StringBuilder_Append(&cbe.sb, "void ");
|
||||
} else if String_Eq(f.retTypeName, "String") || String_Eq(f.retTypeName, "str") {
|
||||
StringBuilder_Append(&cbe.sb, "String ");
|
||||
} else if String_Eq(f.retTypeName, "bool") {
|
||||
StringBuilder_Append(&cbe.sb, "bool ");
|
||||
} else if String_Eq(f.retTypeName, "int") {
|
||||
StringBuilder_Append(&cbe.sb, "int ");
|
||||
} else if String_Eq(f.retTypeName, "int64") {
|
||||
StringBuilder_Append(&cbe.sb, "int64 ");
|
||||
} else if String_Eq(f.retTypeName, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "uint ");
|
||||
} else if String_Eq(f.retTypeName, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "float64 ");
|
||||
} else if f.retTypeKind == tyNamed || !String_Eq(f.retTypeName, "") {
|
||||
// Use the struct name directly (e.g., "Point", "Type")
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, f.retTypeName);
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
|
||||
StringBuilder_Append(&cbe.sb, f.name);
|
||||
|
||||
@@ -76,6 +76,9 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
||||
PrintLine("HIR lowering failed");
|
||||
return "";
|
||||
}
|
||||
Print("HIR funcCount=");
|
||||
PrintInt(hirMod.funcCount);
|
||||
PrintLine("");
|
||||
|
||||
// Phase 5: C code generation
|
||||
let cCode: String = CBackend_Generate(hirMod);
|
||||
|
||||
@@ -31,6 +31,8 @@ const hSliceInit: int = 25;
|
||||
const hRange: int = 26;
|
||||
const hTupleInit: int = 27;
|
||||
const hMatch: int = 28;
|
||||
const hSpawn: int = 29;
|
||||
const hAwait: int = 30;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HirNode — unified struct with tagged union pattern
|
||||
|
||||
@@ -122,6 +122,25 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
return n;
|
||||
}
|
||||
|
||||
// spawn Callee(args)
|
||||
if kind == ekSpawn {
|
||||
n.kind = hSpawn;
|
||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||
n.strValue = expr.child1.strValue;
|
||||
}
|
||||
if expr.child2 != null as *Expr {
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child2);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// expr.await
|
||||
if kind == ekAwait {
|
||||
n.kind = hAwait;
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Index: arr[idx]
|
||||
if kind == ekIndex {
|
||||
n.kind = hIndexPtr;
|
||||
|
||||
@@ -294,6 +294,9 @@ func lexKeywordKind(text: String) -> int {
|
||||
if String_Eq(text, "self") { return tkSelf; }
|
||||
if String_Eq(text, "super") { return tkSuper; }
|
||||
if String_Eq(text, "sizeof") { return tkSizeOf; }
|
||||
if String_Eq(text, "async") { return tkAsync; }
|
||||
if String_Eq(text, "await") { return tkAwait; }
|
||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||
return tkIdent;
|
||||
}
|
||||
|
||||
|
||||
+50
-13
@@ -127,15 +127,9 @@ func parserParseType(p: *Parser) -> *TypeExpr {
|
||||
te.line = line;
|
||||
te.column = col;
|
||||
te.pointerPointee = parserParseType(p);
|
||||
// Set typeName to "String*" for String pointer, else "int*"
|
||||
// Set typeName to "Pointee*"
|
||||
if te.pointerPointee != null as *TypeExpr {
|
||||
if String_Eq(te.pointerPointee.typeName, "String") {
|
||||
te.typeName = "String*";
|
||||
} else if String_Eq(te.pointerPointee.typeName, "int") {
|
||||
te.typeName = "int*";
|
||||
} else {
|
||||
te.typeName = "int*";
|
||||
}
|
||||
te.typeName = String_Concat(te.pointerPointee.typeName, "*");
|
||||
}
|
||||
return te;
|
||||
}
|
||||
@@ -241,8 +235,32 @@ func parserParsePrimary(p: *Parser) -> *Expr {
|
||||
// sizeof(Type)
|
||||
if kind == tkSizeOf {
|
||||
discard parserAdvance(p);
|
||||
discard parserExpect(p, tkLParen, "expected '(' after sizeof");
|
||||
let e: *Expr = parserMakeExpr(ekSizeOf, line, col);
|
||||
e.refType = parserParseType(p);
|
||||
discard parserExpect(p, tkRParen, "expected ')' after sizeof type");
|
||||
return e;
|
||||
}
|
||||
|
||||
// spawn Callee(args)
|
||||
if kind == tkSpawn {
|
||||
discard parserAdvance(p);
|
||||
let e: *Expr = parserMakeExpr(ekSpawn, line, col);
|
||||
e.child1 = parserParsePrimary(p);
|
||||
// Optional call arguments
|
||||
if parserCheck(p, tkLParen) {
|
||||
discard parserAdvance(p);
|
||||
if !parserCheck(p, tkRParen) {
|
||||
e.child2 = parserParseExpr(p);
|
||||
if parserMatch(p, tkComma) {
|
||||
e.child3 = parserParseExpr(p);
|
||||
while parserMatch(p, tkComma) {
|
||||
discard parserParseExpr(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
discard parserExpect(p, tkRParen, "expected ')' after spawn arguments");
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -322,6 +340,20 @@ func parserParsePostfix(p: *Parser) -> *Expr {
|
||||
continue;
|
||||
}
|
||||
|
||||
// .await
|
||||
if kind == tkDot {
|
||||
if parserPeek(p, 1) == tkAwait {
|
||||
discard parserAdvance(p); // .
|
||||
discard parserAdvance(p); // await
|
||||
let line: uint32 = parserCurToken(p).line;
|
||||
let col: uint32 = parserCurToken(p).column;
|
||||
let e: *Expr = parserMakeExpr(ekAwait, line, col);
|
||||
e.child1 = left;
|
||||
left = e;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Field: expr.name
|
||||
if kind == tkDot {
|
||||
discard parserAdvance(p);
|
||||
@@ -765,7 +797,7 @@ func parserParseParamList(p: *Parser) -> *Decl {
|
||||
// Declarations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool) -> *Decl {
|
||||
func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bool) -> *Decl {
|
||||
let line: uint32 = parserCurToken(p).line;
|
||||
let col: uint32 = parserCurToken(p).column;
|
||||
discard parserExpect(p, tkFunc, "expected 'func'");
|
||||
@@ -776,6 +808,7 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool) -> *Decl {
|
||||
d.line = line;
|
||||
d.column = col;
|
||||
d.isPublic = isPublic;
|
||||
d.isAsync = isAsync;
|
||||
d.strValue = nameTok.text;
|
||||
|
||||
// Type params <T, U>
|
||||
@@ -953,7 +986,7 @@ func parserParseImportDecl(p: *Parser, isPublic: bool) -> *Decl {
|
||||
// Parse path: Std::Io::PrintLine
|
||||
var pathStr: String = "";
|
||||
var segCount: int = 0;
|
||||
while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon)) {
|
||||
while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon) && parserPeek(p, 1) != tkLBrace) {
|
||||
if segCount > 0 {
|
||||
discard parserAdvance(p); // ::
|
||||
if String_Len(pathStr) > 0 {
|
||||
@@ -995,7 +1028,7 @@ func parserParseExternDecl(p: *Parser, isPublic: bool) -> *Decl {
|
||||
discard parserExpect(p, tkExtern, "expected 'extern'");
|
||||
|
||||
if parserCheck(p, tkFunc) {
|
||||
let d: *Decl = parserParseFuncDecl(p, isPublic, true);
|
||||
let d: *Decl = parserParseFuncDecl(p, isPublic, true, false);
|
||||
d.kind = dkExternFunc;
|
||||
return d;
|
||||
}
|
||||
@@ -1016,7 +1049,11 @@ func parserParseDecl(p: *Parser) -> *Decl {
|
||||
let isPublic: bool = parserMatch(p, tkPub);
|
||||
let kind: int = parserPeek(p, 0);
|
||||
|
||||
if kind == tkFunc { return parserParseFuncDecl(p, isPublic, false); }
|
||||
if kind == tkAsync && parserPeek(p, 1) == tkFunc {
|
||||
discard parserAdvance(p); // async
|
||||
return parserParseFuncDecl(p, isPublic, false, true);
|
||||
}
|
||||
if kind == tkFunc { return parserParseFuncDecl(p, isPublic, false, false); }
|
||||
if kind == tkStruct { return parserParseStructDecl(p, isPublic); }
|
||||
if kind == tkEnum { return parserParseEnumDecl(p, isPublic); }
|
||||
if kind == tkImport { return parserParseImportDecl(p, isPublic); }
|
||||
@@ -1048,7 +1085,7 @@ func parserParseDecl(p: *Parser) -> *Decl {
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; }
|
||||
if parserCheck(p, tkFunc) {
|
||||
let m: *Decl = parserParseFuncDecl(p, false, false);
|
||||
let m: *Decl = parserParseFuncDecl(p, false, false, false);
|
||||
// Store method in linked list
|
||||
if d.methodCount == 0 {
|
||||
d.childDecl1 = m;
|
||||
|
||||
@@ -133,6 +133,11 @@ const tkNewLine: int = 99;
|
||||
const tkEndOfFile: int = 100;
|
||||
const tkUnknown: int = 101;
|
||||
|
||||
// Async / concurrency
|
||||
const tkAsync: int = 102;
|
||||
const tkAwait: int = 103;
|
||||
const tkSpawn: int = 104;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Token struct
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -154,6 +159,8 @@ func Token_IsKeyword(kind: int) -> bool {
|
||||
if kind >= tkBreak && kind <= tkMatch { return true; }
|
||||
if kind >= tkFunc && kind <= tkExtern { return true; }
|
||||
if kind >= tkAs && kind <= tkSuper { return true; }
|
||||
if kind == tkSizeOf { return true; }
|
||||
if kind >= tkAsync && kind <= tkSpawn { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user