// sema.bux — Semantic analysis (type checker, ported from sema.nim) // Validates types, resolves identifiers, checks function calls. module Sema { // --------------------------------------------------------------------------- // Sema context // --------------------------------------------------------------------------- struct Sema { module: *Module, scope: *Scope, typeTable: *void, methodTable: *void, diagCount: int, diags: *SemaDiag, hasError: bool, } struct SemaDiag { line: uint32, column: uint32, message: String, } // --------------------------------------------------------------------------- // Diagnostics // --------------------------------------------------------------------------- func Sema_EmitError(sema: *Sema, line: uint32, col: uint32, msg: String) { if sema.diagCount < 256 { sema.diags[sema.diagCount] = SemaDiag { line: line, column: col, message: msg }; sema.diagCount = sema.diagCount + 1; } sema.hasError = true; } // --------------------------------------------------------------------------- // Type resolution from TypeExpr → Type constants // --------------------------------------------------------------------------- func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int { if te == null as *TypeExpr { return tyUnknown; } let name: String = te.typeName; if te.kind == tekPointer { return tyPointer; } if String_Eq(name, "void") { return tyVoid; } if String_Eq(name, "bool") { return tyBool; } if String_Eq(name, "bool8") { return tyBool8; } if String_Eq(name, "bool16") { return tyBool16; } if String_Eq(name, "bool32") { return tyBool32; } if String_Eq(name, "char8") { return tyChar8; } if String_Eq(name, "char16") { return tyChar16; } if String_Eq(name, "char32") { return tyChar32; } if String_Eq(name, "String") { return tyStr; } if String_Eq(name, "str") { return tyStr; } if String_Eq(name, "int8") { return tyInt8; } if String_Eq(name, "int16") { return tyInt16; } if String_Eq(name, "int32") { return tyInt32; } if String_Eq(name, "int64") { return tyInt64; } if String_Eq(name, "int") { return tyInt; } if String_Eq(name, "uint8") { return tyUInt8; } if String_Eq(name, "uint16") { return tyUInt16; } if String_Eq(name, "uint32") { return tyUInt32; } if String_Eq(name, "uint64") { return tyUInt64; } if String_Eq(name, "uint") { return tyUInt; } if String_Eq(name, "float32") { return tyFloat32; } if String_Eq(name, "float64") { return tyFloat64; } if String_Eq(name, "float") { return tyFloat64; } // Check type table for user-defined types (StringMap not yet supported) // TODO: re-enable when StringMap generic is available // if sema.typeTable != null as *void { ... } return tyNamed; // assume named type } // --------------------------------------------------------------------------- // Type predicates // --------------------------------------------------------------------------- func Sema_IsNumeric(kind: int) -> bool { if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; } return kind >= tyInt8 && kind <= tyUInt64; } func Sema_IsBool(kind: int) -> bool { return kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32; } func Sema_TypeName(kind: int) -> String { if kind == tyUnknown { return "?"; } if kind == tyVoid { return "void"; } if kind == tyBool { return "bool"; } if kind == tyInt{ return "int"; } if kind == tyInt64 { return "int64"; } if kind == tyUInt { return "uint"; } if kind == tyFloat64 { return "float64"; } if kind == tyStr { return "String"; } if kind == tyPointer { return "*ptr"; } if kind == tyNamed { return "user-type"; } if kind == tyTypeParam { return "type-param"; } return "?"; } // --------------------------------------------------------------------------- // Expression type checking // --------------------------------------------------------------------------- func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { if expr == null as *Expr { return tyUnknown; } let kind: int = expr.kind; // Literal if kind == ekLiteral { let tk: int = expr.tokKind; if tk == tkIntLiteral { return tyInt; } if tk == tkFloatLiteral { return tyFloat64; } if tk == tkStringLiteral { return tyStr; } if tk == tkBoolLiteral { return tyBool; } if tk == tkCharLiteral { return tyChar32; } if tk == tkNull { return tyPointer; } return tyUnknown; } // Identifier — look up in scope if kind == ekIdent { let sym: Symbol = Scope_Lookup(sema.scope, expr.strValue); if sym.kind == 0 && !String_Eq(sym.name, expr.strValue) { Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier"); return tyUnknown; } return sym.typeKind; } // Binary if kind == ekBinary { let left: int = Sema_CheckExpr(sema, expr.child1); let right: int = Sema_CheckExpr(sema, expr.child2); let op: int = expr.intValue; // Comparison operators return bool if op >= tkEq && op <= tkGe { return tyBool; } // Logical operators return bool if op == tkAmpAmp || op == tkPipePipe || op == tkBang { return tyBool; } // Arithmetic returns wider type if !Sema_IsNumeric(left) || !Sema_IsNumeric(right) { Sema_EmitError(sema, expr.line, expr.column, "arithmetic requires numeric operands"); } if left == tyFloat64 || right == tyFloat64 { return tyFloat64; } return tyInt; } // Unary if kind == ekUnary { let operand: int = Sema_CheckExpr(sema, expr.child1); let op: int = expr.intValue; if op == tkBang { return tyBool; } if op == tkStar { return tyUnknown; } // dereference — resolve pointee if op == tkAmp { return tyPointer; } return operand; } // Call if kind == ekCall { let callee: int = Sema_CheckExpr(sema, expr.child1); if callee == tyUnknown { return tyUnknown; } // Assume callee returns same type (simplified) return callee; } // Ternary if kind == ekTernary { return Sema_CheckExpr(sema, expr.child2); // then type } // Cast — return target type if kind == ekCast { if expr.refType != null as *TypeExpr { return Sema_ResolveType(sema, expr.refType); } return tyUnknown; } // Try (?) if kind == ekTry { let inner: int = Sema_CheckExpr(sema, expr.child1); return inner; // simplified } return tyUnknown; } // --------------------------------------------------------------------------- // Statement checking // --------------------------------------------------------------------------- func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) { if stmt == null as *Stmt { return; } let kind: int = stmt.kind; // Let/var if kind == skLet { let initType: int = Sema_CheckExpr(sema, stmt.child1); // Register variable in scope var sym: Symbol; sym.kind = skVar; sym.name = stmt.strValue; sym.typeKind = initType; sym.isMutable = stmt.boolValue; sym.isPublic = false; discard Scope_Define(sema.scope, sym); return; } // Return if kind == skReturn { if stmt.child1 != null as *Expr { discard Sema_CheckExpr(sema, stmt.child1); } return; } // If if kind == skIf { let condType: int = Sema_CheckExpr(sema, stmt.child1); if !Sema_IsBool(condType) && condType != tyUnknown { Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool"); } return; } // While if kind == skWhile { let condType: int = Sema_CheckExpr(sema, stmt.child1); if !Sema_IsBool(condType) && condType != tyUnknown { Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool"); } return; } // Expression statement if kind == skExpr && stmt.child1 != null as *Expr { discard Sema_CheckExpr(sema, stmt.child1); return; } } // --------------------------------------------------------------------------- // Collect globals (register functions, structs, enums in scope) // --------------------------------------------------------------------------- func Sema_CollectGlobals(sema: *Sema) { var decl: *Decl = sema.module.firstItem; while decl != null as *Decl { let dk: int = decl.kind; // Function if dk == dkFunc { var sym: Symbol; sym.kind = skFunc; sym.name = decl.strValue; sym.typeKind = tyFunc; sym.isPublic = decl.isPublic; discard Scope_Define(sema.scope, sym); } // Struct if dk == dkStruct { var sym: Symbol; sym.kind = skType; sym.name = decl.strValue; sym.typeKind = tyNamed; sym.isPublic = decl.isPublic; discard Scope_Define(sema.scope, sym); } // Enum if dk == dkEnum { var sym: Symbol; sym.kind = skType; sym.name = decl.strValue; sym.typeKind = tyNamed; sym.isPublic = decl.isPublic; discard Scope_Define(sema.scope, sym); } // Extern function if dk == dkExternFunc { var sym: Symbol; sym.kind = skFunc; sym.name = decl.strValue; sym.typeKind = tyFunc; sym.isPublic = true; discard Scope_Define(sema.scope, sym); } decl = decl.childDecl2; } } // --------------------------------------------------------------------------- // Analyze — main entry point // --------------------------------------------------------------------------- func Sema_Analyze(mod: *Module) -> *Sema { let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema; s.module = mod; s.scope = bux_alloc(sizeof(Scope)) as *Scope; s.scope.symbols = bux_alloc(256 as uint * sizeof(Symbol)) as *Symbol; s.scope.count = 0; s.scope.parent = null as *Scope; s.hasError = false; s.diagCount = 0; s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag; s.typeTable = null as *void; s.methodTable = null as *void; // First pass: collect globals Sema_CollectGlobals(s); // Second pass: check function bodies var decl: *Decl = mod.firstItem; while decl != null as *Decl { if decl.kind == dkFunc && decl.refBody != null as *Block { // Create function scope (child of global) var funcScope: Scope = Scope_NewChild(s.scope); // Add type params to scope if decl.typeParamCount >= 1 { var tpSym: Symbol; tpSym.kind = skType; tpSym.name = decl.typeParam0; tpSym.typeKind = tyTypeParam; discard Scope_Define(&funcScope, tpSym); } if decl.typeParamCount >= 2 { var tpSym2: Symbol; tpSym2.kind = skType; tpSym2.name = decl.typeParam1; tpSym2.typeKind = tyTypeParam; discard Scope_Define(&funcScope, tpSym2); } // Add parameters to scope var i: int = 0; while i < decl.paramCount { var pSym: Symbol; pSym.kind = skVar; pSym.typeKind = tyInt; // simplified pSym.isMutable = false; if i == 0 { pSym.name = decl.param0.name; } else if i == 1 { pSym.name = decl.param1.name; } else if i == 2 { pSym.name = decl.param2.name; } else if i == 3 { pSym.name = decl.param3.name; } else if i == 4 { pSym.name = decl.param4.name; } else if i == 5 { pSym.name = decl.param5.name; } discard Scope_Define(&funcScope, pSym); i = i + 1; } // Switch to function scope and check body statements let prevScope: *Scope = s.scope; s.scope = &funcScope; // Check body (simplified — just count statements) var stmtCount: int = decl.refBody.stmtCount; // In a full implementation, iterate statements and check each s.scope = prevScope; } decl = decl.childDecl2; } return s; } func Sema_HasError(sema: *Sema) -> bool { return sema.hasError; } func Sema_DiagCount(sema: *Sema) -> int { return sema.diagCount; } func Sema_Free(sema: *Sema) { bux_free(sema.scope.symbols as *void); bux_free(sema.scope as *void); bux_free(sema.diags as *void); bux_free(sema as *void); } }