Phase 2: Fix sema stub — add full function body type checking
- Iterate function body statements and call Sema_CheckStmt on each - Add Sema_CheckBlock helper for child scope creation in if/while/for/loop blocks - Extend Sema_CheckStmt to handle all statement kinds: skIf, skWhile, skDoWhile, skLoop, skFor, skMatch, skBreak, skContinue, skDecl, skReturn - Extend Sema_CheckExpr with ekAssign, ekField, ekIndex, ekBlock, ekSelf - Fix ekCall to resolve return type from function declaration - Fix ekBinary to handle assignment operators (tkAssign, +=, -=, etc.) - Add Sema_CollectGlobals handling for dkUse (imports), dkConst, dkEnum variants - Add tkColonColon path parsing in parser (Color::Red) - Add decl field to Symbol struct for function/type resolution - Add Sema_ZeroInitSymbol helper to avoid garbage from uninitialized struct fields - Fix concurrency.bux missing Task_Join import All examples now pass buxc2 check. make selfhost succeeds.
This commit is contained in:
@@ -170,6 +170,7 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
PrintLine("Parse failed");
|
PrintLine("Parse failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
PrintLine(" Parse done");
|
||||||
|
|
||||||
// Flatten module wrappers
|
// Flatten module wrappers
|
||||||
var decl2: *Decl = mod.firstItem;
|
var decl2: *Decl = mod.firstItem;
|
||||||
@@ -185,6 +186,14 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
let sema: *Sema = Sema_Analyze(mod);
|
let sema: *Sema = Sema_Analyze(mod);
|
||||||
if Sema_HasError(sema) {
|
if Sema_HasError(sema) {
|
||||||
PrintLine("Sema errors found");
|
PrintLine("Sema errors found");
|
||||||
|
var i: int = 0;
|
||||||
|
while i < Sema_DiagCount(sema) {
|
||||||
|
Print(" line ");
|
||||||
|
PrintInt(sema.diags[i].line as int64);
|
||||||
|
Print(": ");
|
||||||
|
PrintLine(sema.diags[i].message);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -413,6 +413,19 @@ func parserParsePostfix(p: *Parser) -> *Expr {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: expr::name
|
||||||
|
if kind == tkColonColon {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
let line: uint32 = parserCurToken(p).line;
|
||||||
|
let col: uint32 = parserCurToken(p).column;
|
||||||
|
let name: LexToken = parserExpect(p, tkIdent, "expected path segment");
|
||||||
|
let e: *Expr = parserMakeExpr(ekField, line, col);
|
||||||
|
e.child1 = left;
|
||||||
|
e.strValue = name.text;
|
||||||
|
left = e;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// as, is
|
// as, is
|
||||||
if kind == tkAs || kind == tkIs {
|
if kind == tkAs || kind == tkIs {
|
||||||
discard parserAdvance(p);
|
discard parserAdvance(p);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct Symbol {
|
|||||||
typeName: String;
|
typeName: String;
|
||||||
isMutable: bool;
|
isMutable: bool;
|
||||||
isPublic: bool;
|
isPublic: bool;
|
||||||
|
decl: *Decl; // associated declaration (for funcs, structs, enums)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
@@ -90,4 +91,5 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
|
|||||||
func Scope_Free(scope: *Scope) {
|
func Scope_Free(scope: *Scope) {
|
||||||
bux_free(scope.symbols as *void);
|
bux_free(scope.symbols as *void);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+309
-10
@@ -2,8 +2,6 @@
|
|||||||
// Validates types, resolves identifiers, checks function calls.
|
// Validates types, resolves identifiers, checks function calls.
|
||||||
module Sema {
|
module Sema {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -15,6 +13,7 @@ struct Sema {
|
|||||||
diagCount: int;
|
diagCount: int;
|
||||||
diags: *SemaDiag;
|
diags: *SemaDiag;
|
||||||
hasError: bool;
|
hasError: bool;
|
||||||
|
currentRetType: int; // return type of the function being checked
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemaDiag {
|
struct SemaDiag {
|
||||||
@@ -35,6 +34,20 @@ func Sema_EmitError(sema: *Sema, line: uint32, col: uint32, msg: String) {
|
|||||||
sema.hasError = true;
|
sema.hasError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Symbol zero-init helper (bootstrap C backend does not zero-init structs)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_ZeroInitSymbol(sym: *Symbol) {
|
||||||
|
sym.kind = 0;
|
||||||
|
sym.name = "";
|
||||||
|
sym.typeKind = 0;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Type resolution from TypeExpr → Type constants
|
// Type resolution from TypeExpr → Type constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -105,6 +118,23 @@ func Sema_TypeName(kind: int) -> String {
|
|||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Block checking helper
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_CheckBlock(sema: *Sema, block: *Block) {
|
||||||
|
if block == null as *Block { return; }
|
||||||
|
var blockScope: Scope = Scope_NewChild(sema.scope);
|
||||||
|
let prevScope: *Scope = sema.scope;
|
||||||
|
sema.scope = &blockScope;
|
||||||
|
var stmt: *Stmt = block.firstStmt;
|
||||||
|
while stmt != null as *Stmt {
|
||||||
|
Sema_CheckStmt(sema, stmt);
|
||||||
|
stmt = stmt.nextStmt;
|
||||||
|
}
|
||||||
|
sema.scope = prevScope;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Expression type checking
|
// Expression type checking
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -112,6 +142,10 @@ func Sema_TypeName(kind: int) -> String {
|
|||||||
func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||||
if expr == null as *Expr { return tyUnknown; }
|
if expr == null as *Expr { return tyUnknown; }
|
||||||
let kind: int = expr.kind;
|
let kind: int = expr.kind;
|
||||||
|
// Debug: print expr kind
|
||||||
|
// Print("Sema_CheckExpr kind=");
|
||||||
|
// PrintInt(kind as int64);
|
||||||
|
// PrintLine("");
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
if kind == ekLiteral {
|
if kind == ekLiteral {
|
||||||
@@ -141,12 +175,26 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return sym.typeKind;
|
return sym.typeKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// self
|
||||||
|
if kind == ekSelf {
|
||||||
|
let sym: Symbol = Scope_Lookup(sema.scope, "self");
|
||||||
|
if sym.kind == 0 && !String_Eq(sym.name, "self") {
|
||||||
|
Sema_EmitError(sema, expr.line, expr.column, "self outside method");
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
return sym.typeKind;
|
||||||
|
}
|
||||||
|
|
||||||
// Binary
|
// Binary
|
||||||
if kind == ekBinary {
|
if kind == ekBinary {
|
||||||
let left: int = Sema_CheckExpr(sema, expr.child1);
|
let left: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
let right: int = Sema_CheckExpr(sema, expr.child2);
|
let right: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
let op: int = expr.intValue;
|
let op: int = expr.intValue;
|
||||||
|
|
||||||
|
// Assignment operators return target type
|
||||||
|
if op == tkAssign || (op >= tkPlusAssign && op <= tkShrAssign) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
// Comparison operators return bool
|
// Comparison operators return bool
|
||||||
if op >= tkEq && op <= tkGe { return tyBool; }
|
if op >= tkEq && op <= tkGe { return tyBool; }
|
||||||
// Logical operators return bool
|
// Logical operators return bool
|
||||||
@@ -169,12 +217,31 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign
|
||||||
|
if kind == ekAssign {
|
||||||
|
let target: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
|
let value: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
|
// Basic type compatibility (permissive for now)
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
let callee: int = Sema_CheckExpr(sema, expr.child1);
|
discard Sema_CheckExpr(sema, expr.child1);
|
||||||
if callee == tyUnknown { return tyUnknown; }
|
if expr.child2 != null as *Expr {
|
||||||
// Assume callee returns same type (simplified)
|
discard Sema_CheckExpr(sema, expr.child2);
|
||||||
return callee;
|
}
|
||||||
|
if expr.child3 != null as *Expr {
|
||||||
|
discard Sema_CheckExpr(sema, expr.child3);
|
||||||
|
}
|
||||||
|
// Try to resolve return type from function declaration
|
||||||
|
if expr.child1.kind == ekIdent {
|
||||||
|
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||||
|
if sym.kind == skFunc && sym.decl != null as *Decl && sym.decl.retType != null as *TypeExpr {
|
||||||
|
return Sema_ResolveType(sema, sym.decl.retType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ternary
|
// Ternary
|
||||||
@@ -201,6 +268,30 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return tyNamed;
|
return tyNamed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Field access
|
||||||
|
if kind == ekField {
|
||||||
|
discard Sema_CheckExpr(sema, expr.child1);
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index
|
||||||
|
if kind == ekIndex {
|
||||||
|
let obj: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
|
let idx: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
|
if !Sema_IsNumeric(idx) && idx != tyUnknown {
|
||||||
|
Sema_EmitError(sema, expr.line, expr.column, "index must be integer");
|
||||||
|
}
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block expression
|
||||||
|
if kind == ekBlock {
|
||||||
|
if expr.refBlock != null as *Block {
|
||||||
|
Sema_CheckBlock(sema, expr.refBlock);
|
||||||
|
}
|
||||||
|
return tyVoid;
|
||||||
|
}
|
||||||
|
|
||||||
return tyUnknown;
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +317,7 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
}
|
}
|
||||||
sym.isMutable = stmt.boolValue;
|
sym.isMutable = stmt.boolValue;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -233,7 +325,21 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
// Return
|
// Return
|
||||||
if kind == skReturn {
|
if kind == skReturn {
|
||||||
if stmt.child1 != null as *Expr {
|
if stmt.child1 != null as *Expr {
|
||||||
discard Sema_CheckExpr(sema, stmt.child1);
|
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
||||||
|
if retType != sema.currentRetType {
|
||||||
|
// Be permissive: allow numeric widening, pointer/named mismatch
|
||||||
|
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
||||||
|
if retType != sema.currentRetType {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sema.currentRetType != tyVoid && sema.currentRetType != tyUnknown {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "missing return value");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -244,6 +350,8 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
if !Sema_IsBool(condType) && condType != tyUnknown {
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool");
|
Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool");
|
||||||
}
|
}
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtElse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +361,54 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
if !Sema_IsBool(condType) && condType != tyUnknown {
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool");
|
Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool");
|
||||||
}
|
}
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do-while
|
||||||
|
if kind == skDoWhile {
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
let condType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "do-while condition must be bool");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop
|
||||||
|
if kind == skLoop {
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For
|
||||||
|
if kind == skFor {
|
||||||
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
var forScope: Scope = Scope_NewChild(sema.scope);
|
||||||
|
var loopSym: Symbol;
|
||||||
|
loopSym.kind = skVar;
|
||||||
|
loopSym.name = stmt.strValue;
|
||||||
|
loopSym.typeKind = tyUnknown;
|
||||||
|
loopSym.typeName = "";
|
||||||
|
loopSym.isMutable = true;
|
||||||
|
loopSym.isPublic = false;
|
||||||
|
loopSym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(&forScope, loopSym);
|
||||||
|
let prevScope: *Scope = sema.scope;
|
||||||
|
sema.scope = &forScope;
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
sema.scope = prevScope;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match
|
||||||
|
if kind == skMatch {
|
||||||
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break / Continue
|
||||||
|
if kind == skBreak || kind == skContinue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +417,14 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
discard Sema_CheckExpr(sema, stmt.child1);
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decl (nested)
|
||||||
|
if kind == skDecl {
|
||||||
|
if stmt.refStmtDecl != null as *Decl && stmt.refStmtDecl.kind == dkFunc {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "nested functions not yet supported");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -275,40 +439,158 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
// Function
|
// Function
|
||||||
if dk == dkFunc {
|
if dk == dkFunc {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skFunc;
|
sym.kind = skFunc;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyFunc;
|
sym.typeKind = tyFunc;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
if dk == dkStruct {
|
if dk == dkStruct {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skType;
|
sym.kind = skType;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyNamed;
|
sym.typeKind = tyNamed;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum
|
// Enum
|
||||||
if dk == dkEnum {
|
if dk == dkEnum {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skType;
|
sym.kind = skType;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyNamed;
|
sym.typeKind = tyNamed;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
|
||||||
|
// Register enum variants as constants
|
||||||
|
var vi: int = 0;
|
||||||
|
while vi < decl.variantCount && vi < 8 {
|
||||||
|
var v: EnumVariant;
|
||||||
|
if vi == 0 { v = decl.variant0; }
|
||||||
|
else if vi == 1 { v = decl.variant1; }
|
||||||
|
else if vi == 2 { v = decl.variant2; }
|
||||||
|
else if vi == 3 { v = decl.variant3; }
|
||||||
|
else if vi == 4 { v = decl.variant4; }
|
||||||
|
else if vi == 5 { v = decl.variant5; }
|
||||||
|
else if vi == 6 { v = decl.variant6; }
|
||||||
|
else if vi == 7 { v = decl.variant7; }
|
||||||
|
let variantName: String = String_Concat(decl.strValue, "_");
|
||||||
|
let variantName2: String = String_Concat(variantName, v.name);
|
||||||
|
var vSym: Symbol;
|
||||||
|
vSym.kind = skConst;
|
||||||
|
vSym.name = variantName2;
|
||||||
|
vSym.typeKind = tyNamed;
|
||||||
|
vSym.typeName = String_Concat(decl.strValue, "_Tag");
|
||||||
|
vSym.isMutable = false;
|
||||||
|
vSym.isPublic = decl.isPublic;
|
||||||
|
vSym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, vSym);
|
||||||
|
vi = vi + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use (import)
|
||||||
|
if dk == dkUse {
|
||||||
|
if decl.useKind == 2 {
|
||||||
|
// Multi-import: names are comma-separated in useNames
|
||||||
|
// For now, register the whole useNames string as a single func name
|
||||||
|
// (permissive fallback — real fix needs string split)
|
||||||
|
let namesStr: String = decl.useNames;
|
||||||
|
if !String_Eq(namesStr, "") {
|
||||||
|
// Simple split by comma using available string functions
|
||||||
|
var start: uint = 0;
|
||||||
|
var pos: uint = 0;
|
||||||
|
let totalLen: uint = String_Len(namesStr);
|
||||||
|
while pos <= totalLen {
|
||||||
|
let atEnd: bool = pos == totalLen;
|
||||||
|
let isComma: bool = false;
|
||||||
|
if pos < totalLen {
|
||||||
|
// Check if character at pos is comma
|
||||||
|
let chStr: String = bux_str_slice(namesStr, pos, 1);
|
||||||
|
isComma = String_Eq(chStr, ",");
|
||||||
|
}
|
||||||
|
if atEnd || isComma {
|
||||||
|
let nameLen: uint = pos - start;
|
||||||
|
if nameLen > 0 {
|
||||||
|
let name: String = bux_str_slice(namesStr, start, nameLen);
|
||||||
|
var sym: Symbol;
|
||||||
|
sym.kind = skFunc;
|
||||||
|
sym.name = name;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = true;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
start = pos + 1;
|
||||||
|
}
|
||||||
|
pos = pos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single import or glob — add last path segment
|
||||||
|
let path: String = decl.usePath;
|
||||||
|
if !String_Eq(path, "") {
|
||||||
|
// Find last :: segment
|
||||||
|
var lastSeg: String = path;
|
||||||
|
let containsColons: int = bux_str_contains(path, "::");
|
||||||
|
if containsColons != 0 {
|
||||||
|
// Try to get last segment by finding last ::
|
||||||
|
// Use a simple heuristic: slice from various positions
|
||||||
|
let pathLen: uint = String_Len(path);
|
||||||
|
var tryPos: uint = 0;
|
||||||
|
while tryPos < pathLen {
|
||||||
|
let slice: String = bux_str_slice(path, tryPos, pathLen - tryPos);
|
||||||
|
if String_StartsWith(slice, "::") {
|
||||||
|
lastSeg = bux_str_slice(slice, 2, String_Len(slice) - 2);
|
||||||
|
}
|
||||||
|
tryPos = tryPos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sym: Symbol;
|
||||||
|
sym.kind = skFunc;
|
||||||
|
sym.name = lastSeg;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = true;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const
|
||||||
|
if dk == dkConst {
|
||||||
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
|
sym.kind = skConst;
|
||||||
|
sym.name = decl.strValue;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extern function
|
// Extern function
|
||||||
if dk == dkExternFunc {
|
if dk == dkExternFunc {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skFunc;
|
sym.kind = skFunc;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyFunc;
|
sym.typeKind = tyFunc;
|
||||||
sym.isPublic = true;
|
sym.isPublic = true;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +614,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
|
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
|
||||||
s.typeTable = null as *void;
|
s.typeTable = null as *void;
|
||||||
s.methodTable = null as *void;
|
s.methodTable = null as *void;
|
||||||
|
s.currentRetType = tyVoid;
|
||||||
|
|
||||||
// First pass: collect globals
|
// First pass: collect globals
|
||||||
Sema_CollectGlobals(s);
|
Sema_CollectGlobals(s);
|
||||||
@@ -349,6 +632,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
tpSym.kind = skType;
|
tpSym.kind = skType;
|
||||||
tpSym.name = decl.typeParam0;
|
tpSym.name = decl.typeParam0;
|
||||||
tpSym.typeKind = tyTypeParam;
|
tpSym.typeKind = tyTypeParam;
|
||||||
|
tpSym.decl = null as *Decl;
|
||||||
discard Scope_Define(&funcScope, tpSym);
|
discard Scope_Define(&funcScope, tpSym);
|
||||||
}
|
}
|
||||||
if decl.typeParamCount >= 2 {
|
if decl.typeParamCount >= 2 {
|
||||||
@@ -356,6 +640,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
tpSym2.kind = skType;
|
tpSym2.kind = skType;
|
||||||
tpSym2.name = decl.typeParam1;
|
tpSym2.name = decl.typeParam1;
|
||||||
tpSym2.typeKind = tyTypeParam;
|
tpSym2.typeKind = tyTypeParam;
|
||||||
|
tpSym2.decl = null as *Decl;
|
||||||
discard Scope_Define(&funcScope, tpSym2);
|
discard Scope_Define(&funcScope, tpSym2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +655,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
else if i == 4 { p = &decl.param4; }
|
else if i == 4 { p = &decl.param4; }
|
||||||
else if i == 5 { p = &decl.param5; }
|
else if i == 5 { p = &decl.param5; }
|
||||||
var pSym: Symbol;
|
var pSym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&pSym);
|
||||||
pSym.kind = skVar;
|
pSym.kind = skVar;
|
||||||
if p != null as *Param && p.refParamType != null as *TypeExpr {
|
if p != null as *Param && p.refParamType != null as *TypeExpr {
|
||||||
pSym.typeKind = Sema_ResolveType(s, p.refParamType);
|
pSym.typeKind = Sema_ResolveType(s, p.refParamType);
|
||||||
@@ -379,6 +665,8 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
pSym.typeName = "";
|
pSym.typeName = "";
|
||||||
}
|
}
|
||||||
pSym.isMutable = false;
|
pSym.isMutable = false;
|
||||||
|
pSym.isPublic = false;
|
||||||
|
pSym.decl = null as *Decl;
|
||||||
if i == 0 { pSym.name = decl.param0.name; }
|
if i == 0 { pSym.name = decl.param0.name; }
|
||||||
else if i == 1 { pSym.name = decl.param1.name; }
|
else if i == 1 { pSym.name = decl.param1.name; }
|
||||||
else if i == 2 { pSym.name = decl.param2.name; }
|
else if i == 2 { pSym.name = decl.param2.name; }
|
||||||
@@ -393,9 +681,19 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
let prevScope: *Scope = s.scope;
|
let prevScope: *Scope = s.scope;
|
||||||
s.scope = &funcScope;
|
s.scope = &funcScope;
|
||||||
|
|
||||||
// Check body (simplified — just count statements)
|
// Set current function return type
|
||||||
var stmtCount: int = decl.refBody.stmtCount;
|
if decl.retType != null as *TypeExpr {
|
||||||
// In a full implementation, iterate statements and check each
|
s.currentRetType = Sema_ResolveType(s, decl.retType);
|
||||||
|
} else {
|
||||||
|
s.currentRetType = tyVoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check body statements
|
||||||
|
var stmt: *Stmt = decl.refBody.firstStmt;
|
||||||
|
while stmt != null as *Stmt {
|
||||||
|
Sema_CheckStmt(s, stmt);
|
||||||
|
stmt = stmt.nextStmt;
|
||||||
|
}
|
||||||
|
|
||||||
s.scope = prevScope;
|
s.scope = prevScope;
|
||||||
}
|
}
|
||||||
@@ -419,4 +717,5 @@ func Sema_Free(sema: *Sema) {
|
|||||||
bux_free(sema.diags as *void);
|
bux_free(sema.diags as *void);
|
||||||
bux_free(sema as *void);
|
bux_free(sema as *void);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Std::Io::{PrintLine, PrintInt};
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
import Std::Task::TaskHandle;
|
import Std::Task::TaskHandle;
|
||||||
|
import Std::Task::Task_Join;
|
||||||
import Std::Channel::Channel;
|
import Std::Channel::Channel;
|
||||||
|
|
||||||
func Worker(arg: *void) -> *void {
|
func Worker(arg: *void) -> *void {
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
PrintLine("Parse failed");
|
PrintLine("Parse failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
PrintLine(" Parse done");
|
||||||
|
|
||||||
// Flatten module wrappers
|
// Flatten module wrappers
|
||||||
var decl2: *Decl = mod.firstItem;
|
var decl2: *Decl = mod.firstItem;
|
||||||
@@ -185,6 +186,14 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
let sema: *Sema = Sema_Analyze(mod);
|
let sema: *Sema = Sema_Analyze(mod);
|
||||||
if Sema_HasError(sema) {
|
if Sema_HasError(sema) {
|
||||||
PrintLine("Sema errors found");
|
PrintLine("Sema errors found");
|
||||||
|
var i: int = 0;
|
||||||
|
while i < Sema_DiagCount(sema) {
|
||||||
|
Print(" line ");
|
||||||
|
PrintInt(sema.diags[i].line as int64);
|
||||||
|
Print(": ");
|
||||||
|
PrintLine(sema.diags[i].message);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -413,6 +413,19 @@ func parserParsePostfix(p: *Parser) -> *Expr {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path: expr::name
|
||||||
|
if kind == tkColonColon {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
let line: uint32 = parserCurToken(p).line;
|
||||||
|
let col: uint32 = parserCurToken(p).column;
|
||||||
|
let name: LexToken = parserExpect(p, tkIdent, "expected path segment");
|
||||||
|
let e: *Expr = parserMakeExpr(ekField, line, col);
|
||||||
|
e.child1 = left;
|
||||||
|
e.strValue = name.text;
|
||||||
|
left = e;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// as, is
|
// as, is
|
||||||
if kind == tkAs || kind == tkIs {
|
if kind == tkAs || kind == tkIs {
|
||||||
discard parserAdvance(p);
|
discard parserAdvance(p);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct Symbol {
|
|||||||
typeName: String;
|
typeName: String;
|
||||||
isMutable: bool;
|
isMutable: bool;
|
||||||
isPublic: bool;
|
isPublic: bool;
|
||||||
|
decl: *Decl; // associated declaration (for funcs, structs, enums)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
@@ -90,4 +91,5 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
|
|||||||
func Scope_Free(scope: *Scope) {
|
func Scope_Free(scope: *Scope) {
|
||||||
bux_free(scope.symbols as *void);
|
bux_free(scope.symbols as *void);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+309
-10
@@ -2,8 +2,6 @@
|
|||||||
// Validates types, resolves identifiers, checks function calls.
|
// Validates types, resolves identifiers, checks function calls.
|
||||||
module Sema {
|
module Sema {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -15,6 +13,7 @@ struct Sema {
|
|||||||
diagCount: int;
|
diagCount: int;
|
||||||
diags: *SemaDiag;
|
diags: *SemaDiag;
|
||||||
hasError: bool;
|
hasError: bool;
|
||||||
|
currentRetType: int; // return type of the function being checked
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemaDiag {
|
struct SemaDiag {
|
||||||
@@ -35,6 +34,20 @@ func Sema_EmitError(sema: *Sema, line: uint32, col: uint32, msg: String) {
|
|||||||
sema.hasError = true;
|
sema.hasError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Symbol zero-init helper (bootstrap C backend does not zero-init structs)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_ZeroInitSymbol(sym: *Symbol) {
|
||||||
|
sym.kind = 0;
|
||||||
|
sym.name = "";
|
||||||
|
sym.typeKind = 0;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Type resolution from TypeExpr → Type constants
|
// Type resolution from TypeExpr → Type constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -105,6 +118,23 @@ func Sema_TypeName(kind: int) -> String {
|
|||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Block checking helper
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_CheckBlock(sema: *Sema, block: *Block) {
|
||||||
|
if block == null as *Block { return; }
|
||||||
|
var blockScope: Scope = Scope_NewChild(sema.scope);
|
||||||
|
let prevScope: *Scope = sema.scope;
|
||||||
|
sema.scope = &blockScope;
|
||||||
|
var stmt: *Stmt = block.firstStmt;
|
||||||
|
while stmt != null as *Stmt {
|
||||||
|
Sema_CheckStmt(sema, stmt);
|
||||||
|
stmt = stmt.nextStmt;
|
||||||
|
}
|
||||||
|
sema.scope = prevScope;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Expression type checking
|
// Expression type checking
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -112,6 +142,10 @@ func Sema_TypeName(kind: int) -> String {
|
|||||||
func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||||
if expr == null as *Expr { return tyUnknown; }
|
if expr == null as *Expr { return tyUnknown; }
|
||||||
let kind: int = expr.kind;
|
let kind: int = expr.kind;
|
||||||
|
// Debug: print expr kind
|
||||||
|
// Print("Sema_CheckExpr kind=");
|
||||||
|
// PrintInt(kind as int64);
|
||||||
|
// PrintLine("");
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
if kind == ekLiteral {
|
if kind == ekLiteral {
|
||||||
@@ -141,12 +175,26 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return sym.typeKind;
|
return sym.typeKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// self
|
||||||
|
if kind == ekSelf {
|
||||||
|
let sym: Symbol = Scope_Lookup(sema.scope, "self");
|
||||||
|
if sym.kind == 0 && !String_Eq(sym.name, "self") {
|
||||||
|
Sema_EmitError(sema, expr.line, expr.column, "self outside method");
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
return sym.typeKind;
|
||||||
|
}
|
||||||
|
|
||||||
// Binary
|
// Binary
|
||||||
if kind == ekBinary {
|
if kind == ekBinary {
|
||||||
let left: int = Sema_CheckExpr(sema, expr.child1);
|
let left: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
let right: int = Sema_CheckExpr(sema, expr.child2);
|
let right: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
let op: int = expr.intValue;
|
let op: int = expr.intValue;
|
||||||
|
|
||||||
|
// Assignment operators return target type
|
||||||
|
if op == tkAssign || (op >= tkPlusAssign && op <= tkShrAssign) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
// Comparison operators return bool
|
// Comparison operators return bool
|
||||||
if op >= tkEq && op <= tkGe { return tyBool; }
|
if op >= tkEq && op <= tkGe { return tyBool; }
|
||||||
// Logical operators return bool
|
// Logical operators return bool
|
||||||
@@ -169,12 +217,31 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign
|
||||||
|
if kind == ekAssign {
|
||||||
|
let target: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
|
let value: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
|
// Basic type compatibility (permissive for now)
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
let callee: int = Sema_CheckExpr(sema, expr.child1);
|
discard Sema_CheckExpr(sema, expr.child1);
|
||||||
if callee == tyUnknown { return tyUnknown; }
|
if expr.child2 != null as *Expr {
|
||||||
// Assume callee returns same type (simplified)
|
discard Sema_CheckExpr(sema, expr.child2);
|
||||||
return callee;
|
}
|
||||||
|
if expr.child3 != null as *Expr {
|
||||||
|
discard Sema_CheckExpr(sema, expr.child3);
|
||||||
|
}
|
||||||
|
// Try to resolve return type from function declaration
|
||||||
|
if expr.child1.kind == ekIdent {
|
||||||
|
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||||
|
if sym.kind == skFunc && sym.decl != null as *Decl && sym.decl.retType != null as *TypeExpr {
|
||||||
|
return Sema_ResolveType(sema, sym.decl.retType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ternary
|
// Ternary
|
||||||
@@ -201,6 +268,30 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return tyNamed;
|
return tyNamed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Field access
|
||||||
|
if kind == ekField {
|
||||||
|
discard Sema_CheckExpr(sema, expr.child1);
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index
|
||||||
|
if kind == ekIndex {
|
||||||
|
let obj: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
|
let idx: int = Sema_CheckExpr(sema, expr.child2);
|
||||||
|
if !Sema_IsNumeric(idx) && idx != tyUnknown {
|
||||||
|
Sema_EmitError(sema, expr.line, expr.column, "index must be integer");
|
||||||
|
}
|
||||||
|
return tyUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block expression
|
||||||
|
if kind == ekBlock {
|
||||||
|
if expr.refBlock != null as *Block {
|
||||||
|
Sema_CheckBlock(sema, expr.refBlock);
|
||||||
|
}
|
||||||
|
return tyVoid;
|
||||||
|
}
|
||||||
|
|
||||||
return tyUnknown;
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +317,7 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
}
|
}
|
||||||
sym.isMutable = stmt.boolValue;
|
sym.isMutable = stmt.boolValue;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -233,7 +325,21 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
// Return
|
// Return
|
||||||
if kind == skReturn {
|
if kind == skReturn {
|
||||||
if stmt.child1 != null as *Expr {
|
if stmt.child1 != null as *Expr {
|
||||||
discard Sema_CheckExpr(sema, stmt.child1);
|
let retType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
if sema.currentRetType != tyUnknown && retType != tyUnknown {
|
||||||
|
if retType != sema.currentRetType {
|
||||||
|
// Be permissive: allow numeric widening, pointer/named mismatch
|
||||||
|
if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) {
|
||||||
|
if retType != sema.currentRetType {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sema.currentRetType != tyVoid && sema.currentRetType != tyUnknown {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "missing return value");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -244,6 +350,8 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
if !Sema_IsBool(condType) && condType != tyUnknown {
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool");
|
Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool");
|
||||||
}
|
}
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtElse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +361,54 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
if !Sema_IsBool(condType) && condType != tyUnknown {
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool");
|
Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool");
|
||||||
}
|
}
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do-while
|
||||||
|
if kind == skDoWhile {
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
let condType: int = Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
if !Sema_IsBool(condType) && condType != tyUnknown {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "do-while condition must be bool");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop
|
||||||
|
if kind == skLoop {
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For
|
||||||
|
if kind == skFor {
|
||||||
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
var forScope: Scope = Scope_NewChild(sema.scope);
|
||||||
|
var loopSym: Symbol;
|
||||||
|
loopSym.kind = skVar;
|
||||||
|
loopSym.name = stmt.strValue;
|
||||||
|
loopSym.typeKind = tyUnknown;
|
||||||
|
loopSym.typeName = "";
|
||||||
|
loopSym.isMutable = true;
|
||||||
|
loopSym.isPublic = false;
|
||||||
|
loopSym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(&forScope, loopSym);
|
||||||
|
let prevScope: *Scope = sema.scope;
|
||||||
|
sema.scope = &forScope;
|
||||||
|
Sema_CheckBlock(sema, stmt.refStmtBlock);
|
||||||
|
sema.scope = prevScope;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match
|
||||||
|
if kind == skMatch {
|
||||||
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break / Continue
|
||||||
|
if kind == skBreak || kind == skContinue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +417,14 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
discard Sema_CheckExpr(sema, stmt.child1);
|
discard Sema_CheckExpr(sema, stmt.child1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decl (nested)
|
||||||
|
if kind == skDecl {
|
||||||
|
if stmt.refStmtDecl != null as *Decl && stmt.refStmtDecl.kind == dkFunc {
|
||||||
|
Sema_EmitError(sema, stmt.line, stmt.column, "nested functions not yet supported");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -275,40 +439,158 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
// Function
|
// Function
|
||||||
if dk == dkFunc {
|
if dk == dkFunc {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skFunc;
|
sym.kind = skFunc;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyFunc;
|
sym.typeKind = tyFunc;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
if dk == dkStruct {
|
if dk == dkStruct {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skType;
|
sym.kind = skType;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyNamed;
|
sym.typeKind = tyNamed;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum
|
// Enum
|
||||||
if dk == dkEnum {
|
if dk == dkEnum {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skType;
|
sym.kind = skType;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyNamed;
|
sym.typeKind = tyNamed;
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
|
||||||
|
// Register enum variants as constants
|
||||||
|
var vi: int = 0;
|
||||||
|
while vi < decl.variantCount && vi < 8 {
|
||||||
|
var v: EnumVariant;
|
||||||
|
if vi == 0 { v = decl.variant0; }
|
||||||
|
else if vi == 1 { v = decl.variant1; }
|
||||||
|
else if vi == 2 { v = decl.variant2; }
|
||||||
|
else if vi == 3 { v = decl.variant3; }
|
||||||
|
else if vi == 4 { v = decl.variant4; }
|
||||||
|
else if vi == 5 { v = decl.variant5; }
|
||||||
|
else if vi == 6 { v = decl.variant6; }
|
||||||
|
else if vi == 7 { v = decl.variant7; }
|
||||||
|
let variantName: String = String_Concat(decl.strValue, "_");
|
||||||
|
let variantName2: String = String_Concat(variantName, v.name);
|
||||||
|
var vSym: Symbol;
|
||||||
|
vSym.kind = skConst;
|
||||||
|
vSym.name = variantName2;
|
||||||
|
vSym.typeKind = tyNamed;
|
||||||
|
vSym.typeName = String_Concat(decl.strValue, "_Tag");
|
||||||
|
vSym.isMutable = false;
|
||||||
|
vSym.isPublic = decl.isPublic;
|
||||||
|
vSym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, vSym);
|
||||||
|
vi = vi + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use (import)
|
||||||
|
if dk == dkUse {
|
||||||
|
if decl.useKind == 2 {
|
||||||
|
// Multi-import: names are comma-separated in useNames
|
||||||
|
// For now, register the whole useNames string as a single func name
|
||||||
|
// (permissive fallback — real fix needs string split)
|
||||||
|
let namesStr: String = decl.useNames;
|
||||||
|
if !String_Eq(namesStr, "") {
|
||||||
|
// Simple split by comma using available string functions
|
||||||
|
var start: uint = 0;
|
||||||
|
var pos: uint = 0;
|
||||||
|
let totalLen: uint = String_Len(namesStr);
|
||||||
|
while pos <= totalLen {
|
||||||
|
let atEnd: bool = pos == totalLen;
|
||||||
|
let isComma: bool = false;
|
||||||
|
if pos < totalLen {
|
||||||
|
// Check if character at pos is comma
|
||||||
|
let chStr: String = bux_str_slice(namesStr, pos, 1);
|
||||||
|
isComma = String_Eq(chStr, ",");
|
||||||
|
}
|
||||||
|
if atEnd || isComma {
|
||||||
|
let nameLen: uint = pos - start;
|
||||||
|
if nameLen > 0 {
|
||||||
|
let name: String = bux_str_slice(namesStr, start, nameLen);
|
||||||
|
var sym: Symbol;
|
||||||
|
sym.kind = skFunc;
|
||||||
|
sym.name = name;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = true;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
start = pos + 1;
|
||||||
|
}
|
||||||
|
pos = pos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single import or glob — add last path segment
|
||||||
|
let path: String = decl.usePath;
|
||||||
|
if !String_Eq(path, "") {
|
||||||
|
// Find last :: segment
|
||||||
|
var lastSeg: String = path;
|
||||||
|
let containsColons: int = bux_str_contains(path, "::");
|
||||||
|
if containsColons != 0 {
|
||||||
|
// Try to get last segment by finding last ::
|
||||||
|
// Use a simple heuristic: slice from various positions
|
||||||
|
let pathLen: uint = String_Len(path);
|
||||||
|
var tryPos: uint = 0;
|
||||||
|
while tryPos < pathLen {
|
||||||
|
let slice: String = bux_str_slice(path, tryPos, pathLen - tryPos);
|
||||||
|
if String_StartsWith(slice, "::") {
|
||||||
|
lastSeg = bux_str_slice(slice, 2, String_Len(slice) - 2);
|
||||||
|
}
|
||||||
|
tryPos = tryPos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sym: Symbol;
|
||||||
|
sym.kind = skFunc;
|
||||||
|
sym.name = lastSeg;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = true;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const
|
||||||
|
if dk == dkConst {
|
||||||
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
|
sym.kind = skConst;
|
||||||
|
sym.name = decl.strValue;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extern function
|
// Extern function
|
||||||
if dk == dkExternFunc {
|
if dk == dkExternFunc {
|
||||||
var sym: Symbol;
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
sym.kind = skFunc;
|
sym.kind = skFunc;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyFunc;
|
sym.typeKind = tyFunc;
|
||||||
sym.isPublic = true;
|
sym.isPublic = true;
|
||||||
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +614,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
|
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
|
||||||
s.typeTable = null as *void;
|
s.typeTable = null as *void;
|
||||||
s.methodTable = null as *void;
|
s.methodTable = null as *void;
|
||||||
|
s.currentRetType = tyVoid;
|
||||||
|
|
||||||
// First pass: collect globals
|
// First pass: collect globals
|
||||||
Sema_CollectGlobals(s);
|
Sema_CollectGlobals(s);
|
||||||
@@ -349,6 +632,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
tpSym.kind = skType;
|
tpSym.kind = skType;
|
||||||
tpSym.name = decl.typeParam0;
|
tpSym.name = decl.typeParam0;
|
||||||
tpSym.typeKind = tyTypeParam;
|
tpSym.typeKind = tyTypeParam;
|
||||||
|
tpSym.decl = null as *Decl;
|
||||||
discard Scope_Define(&funcScope, tpSym);
|
discard Scope_Define(&funcScope, tpSym);
|
||||||
}
|
}
|
||||||
if decl.typeParamCount >= 2 {
|
if decl.typeParamCount >= 2 {
|
||||||
@@ -356,6 +640,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
tpSym2.kind = skType;
|
tpSym2.kind = skType;
|
||||||
tpSym2.name = decl.typeParam1;
|
tpSym2.name = decl.typeParam1;
|
||||||
tpSym2.typeKind = tyTypeParam;
|
tpSym2.typeKind = tyTypeParam;
|
||||||
|
tpSym2.decl = null as *Decl;
|
||||||
discard Scope_Define(&funcScope, tpSym2);
|
discard Scope_Define(&funcScope, tpSym2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +655,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
else if i == 4 { p = &decl.param4; }
|
else if i == 4 { p = &decl.param4; }
|
||||||
else if i == 5 { p = &decl.param5; }
|
else if i == 5 { p = &decl.param5; }
|
||||||
var pSym: Symbol;
|
var pSym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&pSym);
|
||||||
pSym.kind = skVar;
|
pSym.kind = skVar;
|
||||||
if p != null as *Param && p.refParamType != null as *TypeExpr {
|
if p != null as *Param && p.refParamType != null as *TypeExpr {
|
||||||
pSym.typeKind = Sema_ResolveType(s, p.refParamType);
|
pSym.typeKind = Sema_ResolveType(s, p.refParamType);
|
||||||
@@ -379,6 +665,8 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
pSym.typeName = "";
|
pSym.typeName = "";
|
||||||
}
|
}
|
||||||
pSym.isMutable = false;
|
pSym.isMutable = false;
|
||||||
|
pSym.isPublic = false;
|
||||||
|
pSym.decl = null as *Decl;
|
||||||
if i == 0 { pSym.name = decl.param0.name; }
|
if i == 0 { pSym.name = decl.param0.name; }
|
||||||
else if i == 1 { pSym.name = decl.param1.name; }
|
else if i == 1 { pSym.name = decl.param1.name; }
|
||||||
else if i == 2 { pSym.name = decl.param2.name; }
|
else if i == 2 { pSym.name = decl.param2.name; }
|
||||||
@@ -393,9 +681,19 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
let prevScope: *Scope = s.scope;
|
let prevScope: *Scope = s.scope;
|
||||||
s.scope = &funcScope;
|
s.scope = &funcScope;
|
||||||
|
|
||||||
// Check body (simplified — just count statements)
|
// Set current function return type
|
||||||
var stmtCount: int = decl.refBody.stmtCount;
|
if decl.retType != null as *TypeExpr {
|
||||||
// In a full implementation, iterate statements and check each
|
s.currentRetType = Sema_ResolveType(s, decl.retType);
|
||||||
|
} else {
|
||||||
|
s.currentRetType = tyVoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check body statements
|
||||||
|
var stmt: *Stmt = decl.refBody.firstStmt;
|
||||||
|
while stmt != null as *Stmt {
|
||||||
|
Sema_CheckStmt(s, stmt);
|
||||||
|
stmt = stmt.nextStmt;
|
||||||
|
}
|
||||||
|
|
||||||
s.scope = prevScope;
|
s.scope = prevScope;
|
||||||
}
|
}
|
||||||
@@ -419,4 +717,5 @@ func Sema_Free(sema: *Sema) {
|
|||||||
bux_free(sema.diags as *void);
|
bux_free(sema.diags as *void);
|
||||||
bux_free(sema as *void);
|
bux_free(sema as *void);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user