feat(selfhost): function types (func(Params) -> Ret)
- Add tekFunc AST node and TypeExprList linked list for params - Parse func(T, U) -> R syntax in parser.bux - Resolve tekFunc to tyFunc in sema.bux - Track original TypeExpr via Symbol.refType for params/lets/consts/funcs - Build C function-pointer type names in hir_lower.bux (Ret (*)(Params)) - Lower indirect calls through function-typed values as hCallIndirect - Add CBE_CParamDecl helper in c_backend.bux to embed name inside (*) - Emit hCallIndirect and function-pointer variable declarations - _test_funcptr now builds and runs with selfhost buxc2
This commit is contained in:
+10
-1
@@ -28,12 +28,18 @@ const tekSlice: int = 2;
|
|||||||
const tekPointer: int = 3;
|
const tekPointer: int = 3;
|
||||||
const tekTuple: int = 4;
|
const tekTuple: int = 4;
|
||||||
const tekSelf: int = 5;
|
const tekSelf: int = 5;
|
||||||
|
const tekFunc: int = 6;
|
||||||
|
|
||||||
|
struct TypeExprList {
|
||||||
|
te: *TypeExpr,
|
||||||
|
next: *TypeExprList,
|
||||||
|
}
|
||||||
|
|
||||||
struct TypeExpr {
|
struct TypeExpr {
|
||||||
kind: int,
|
kind: int,
|
||||||
line: uint32,
|
line: uint32,
|
||||||
column: uint32,
|
column: uint32,
|
||||||
typeName: String, // for tekNamed
|
typeName: String, // for tekNamed / diagnostic name for tekFunc
|
||||||
pathStr: String, // for tekPath (segments joined with ::)
|
pathStr: String, // for tekPath (segments joined with ::)
|
||||||
pathCount: int, // number of path segments
|
pathCount: int, // number of path segments
|
||||||
typeArgName0: String, // up to 2 type args
|
typeArgName0: String, // up to 2 type args
|
||||||
@@ -41,6 +47,9 @@ struct TypeExpr {
|
|||||||
typeArgCount: int,
|
typeArgCount: int,
|
||||||
sliceElement: *TypeExpr, // for tekSlice
|
sliceElement: *TypeExpr, // for tekSlice
|
||||||
pointerPointee: *TypeExpr, // for tekPointer
|
pointerPointee: *TypeExpr, // for tekPointer
|
||||||
|
funcParams: *TypeExprList, // for tekFunc
|
||||||
|
funcRet: *TypeExpr, // for tekFunc
|
||||||
|
funcParamCount: int, // for tekFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+52
-29
@@ -31,9 +31,19 @@ func CBackend_TypeToC(kind: int) -> String {
|
|||||||
if kind == tyFloat64 { return "float64"; }
|
if kind == tyFloat64 { return "float64"; }
|
||||||
if kind == tyPointer { return "void*"; }
|
if kind == tyPointer { return "void*"; }
|
||||||
if kind == tyNamed { return "int"; }
|
if kind == tyNamed { return "int"; }
|
||||||
|
if kind == tyFunc { return "void (*)(void)"; }
|
||||||
return "int";
|
return "int";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit a C parameter/variable declaration, embedding the name inside function-pointer syntax.
|
||||||
|
func CBE_CParamDecl(typeStr: String, name: String) -> String {
|
||||||
|
if String_Contains(typeStr, "(*)") {
|
||||||
|
let replacement: String = String_Concat("(*", String_Concat(name, ")"));
|
||||||
|
return String_Replace(typeStr, "(*)", replacement);
|
||||||
|
}
|
||||||
|
return String_Concat(typeStr, String_Concat(" ", name));
|
||||||
|
}
|
||||||
|
|
||||||
func CBackend_OpToC(op: int) -> String {
|
func CBackend_OpToC(op: int) -> String {
|
||||||
if op == tkPlus { return "+"; }
|
if op == tkPlus { return "+"; }
|
||||||
if op == tkMinus { return "-"; }
|
if op == tkMinus { return "-"; }
|
||||||
@@ -263,6 +273,38 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indirect call through function pointer
|
||||||
|
if kind == hCallIndirect {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, "(");
|
||||||
|
var needsComma: bool = false;
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
needsComma = true;
|
||||||
|
}
|
||||||
|
if node.child3 != null as *HirNode {
|
||||||
|
if needsComma {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
|
}
|
||||||
|
CBE_EmitExpr(cbe, node.child3);
|
||||||
|
needsComma = true;
|
||||||
|
}
|
||||||
|
// Emit extra args from linked list
|
||||||
|
var ai: int = 0;
|
||||||
|
var curExtra: *HirArgList = node.extraData as *HirArgList;
|
||||||
|
while ai < node.extraCount {
|
||||||
|
if needsComma {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
|
}
|
||||||
|
CBE_EmitExpr(cbe, curExtra.node);
|
||||||
|
needsComma = true;
|
||||||
|
curExtra = curExtra.next;
|
||||||
|
ai = ai + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// spawn Callee(args)
|
// spawn Callee(args)
|
||||||
if kind == hSpawn {
|
if kind == hSpawn {
|
||||||
StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
|
StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
|
||||||
@@ -310,13 +352,11 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
|
|
||||||
// Alloca
|
// Alloca
|
||||||
if kind == hAlloca {
|
if kind == hAlloca {
|
||||||
|
var ct: String = "int";
|
||||||
if !String_Eq(node.typeName, "") {
|
if !String_Eq(node.typeName, "") {
|
||||||
StringBuilder_Append(&cbe.sb, node.typeName);
|
ct = node.typeName;
|
||||||
} else {
|
|
||||||
StringBuilder_Append(&cbe.sb, "int");
|
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.strValue));
|
||||||
StringBuilder_Append(&cbe.sb, node.strValue);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,13 +364,11 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
if kind == hStore {
|
if kind == hStore {
|
||||||
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||||
// Declaration with initializer: Type x = value;
|
// Declaration with initializer: Type x = value;
|
||||||
|
var ct: String = CBackend_TypeToC(node.child1.intValue);
|
||||||
if !String_Eq(node.child1.typeName, "") {
|
if !String_Eq(node.child1.typeName, "") {
|
||||||
StringBuilder_Append(&cbe.sb, node.child1.typeName);
|
ct = node.child1.typeName;
|
||||||
} else {
|
|
||||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.child1.intValue));
|
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.child1.strValue));
|
||||||
StringBuilder_Append(&cbe.sb, node.child1.strValue);
|
|
||||||
if node.child2 != null as *HirNode {
|
if node.child2 != null as *HirNode {
|
||||||
StringBuilder_Append(&cbe.sb, " = ");
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
CBE_EmitExpr(cbe, node.child2);
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
@@ -624,26 +662,11 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
|||||||
if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; }
|
if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; }
|
||||||
if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; }
|
if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; }
|
||||||
if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; }
|
if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; }
|
||||||
// Emit type
|
// Emit type + name
|
||||||
if String_Eq(ptype, "String") || String_Eq(ptype, "str") {
|
if String_Eq(ptype, "") {
|
||||||
StringBuilder_Append(&cbe.sb, "String ");
|
ptype = "int";
|
||||||
} else if String_Eq(ptype, "bool") {
|
|
||||||
StringBuilder_Append(&cbe.sb, "bool ");
|
|
||||||
} else if String_Eq(ptype, "int") {
|
|
||||||
StringBuilder_Append(&cbe.sb, "int ");
|
|
||||||
} else if String_Eq(ptype, "int64") {
|
|
||||||
StringBuilder_Append(&cbe.sb, "int64 ");
|
|
||||||
} else if String_Eq(ptype, "uint") {
|
|
||||||
StringBuilder_Append(&cbe.sb, "uint ");
|
|
||||||
} else if String_Eq(ptype, "float64") {
|
|
||||||
StringBuilder_Append(&cbe.sb, "float64 ");
|
|
||||||
} else if !String_Eq(ptype, "") {
|
|
||||||
StringBuilder_Append(&cbe.sb, ptype);
|
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
|
||||||
} else {
|
|
||||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, pname);
|
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ptype, pname));
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
-11
@@ -56,10 +56,46 @@ func Lcx_ResolveTypeKind(te: *TypeExpr) -> int {
|
|||||||
if te.kind == tekPointer { return tyPointer; }
|
if te.kind == tekPointer { return tyPointer; }
|
||||||
if te.kind == tekSlice { return tySlice; }
|
if te.kind == tekSlice { return tySlice; }
|
||||||
if te.kind == tekTuple { return tyTuple; }
|
if te.kind == tekTuple { return tyTuple; }
|
||||||
|
if te.kind == tekFunc { return tyFunc; }
|
||||||
|
|
||||||
return Lcx_ResolveTypeKindFromName(te.typeName);
|
return Lcx_ResolveTypeKindFromName(te.typeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build C function-pointer type string from a tekFunc TypeExpr, e.g. "int (*)(int)"
|
||||||
|
func Lcx_BuildFuncTypeName(te: *TypeExpr) -> String {
|
||||||
|
if te == null as *TypeExpr || te.kind != tekFunc { return "void (*)(void)"; }
|
||||||
|
var retName: String = "void";
|
||||||
|
if te.funcRet != null as *TypeExpr {
|
||||||
|
if te.funcRet.kind == tekPointer && te.funcRet.pointerPointee != null as *TypeExpr {
|
||||||
|
retName = String_Concat(te.funcRet.pointerPointee.typeName, "*");
|
||||||
|
} else {
|
||||||
|
retName = te.funcRet.typeName;
|
||||||
|
}
|
||||||
|
if String_Eq(retName, "") { retName = "int"; }
|
||||||
|
}
|
||||||
|
var result: String = retName;
|
||||||
|
result = String_Concat(result, " (*)(");
|
||||||
|
var cur: *TypeExprList = te.funcParams;
|
||||||
|
var first: bool = true;
|
||||||
|
while cur != null as *TypeExprList {
|
||||||
|
if !first {
|
||||||
|
result = String_Concat(result, ", ");
|
||||||
|
}
|
||||||
|
var pName: String = "int";
|
||||||
|
if cur.te.kind == tekPointer && cur.te.pointerPointee != null as *TypeExpr {
|
||||||
|
pName = String_Concat(cur.te.pointerPointee.typeName, "*");
|
||||||
|
} else {
|
||||||
|
pName = cur.te.typeName;
|
||||||
|
}
|
||||||
|
if String_Eq(pName, "") { pName = "int"; }
|
||||||
|
result = String_Concat(result, pName);
|
||||||
|
first = false;
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
result = String_Concat(result, ")");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Fresh name generation
|
// Fresh name generation
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -152,15 +188,18 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
if expr.intValue == tkAmp {
|
if expr.intValue == tkAmp {
|
||||||
n.typeKind = tyPointer;
|
n.typeKind = tyPointer;
|
||||||
|
if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc {
|
||||||
|
n.typeName = Lcx_BuildFuncTypeName(expr.child1.refType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
n.kind = hCall;
|
|
||||||
// Method call desugaring: obj.method(args) → Type_method(obj, args)
|
// Method call desugaring: obj.method(args) → Type_method(obj, args)
|
||||||
if expr.child1 != null as *Expr && expr.child1.kind == ekField {
|
if expr.child1 != null as *Expr && expr.child1.kind == ekField {
|
||||||
|
n.kind = hCall;
|
||||||
let methodName: String = expr.child1.strValue;
|
let methodName: String = expr.child1.strValue;
|
||||||
var receiverTypeName: String = "";
|
var receiverTypeName: String = "";
|
||||||
if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent {
|
if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent {
|
||||||
@@ -217,11 +256,20 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
// Get callee name
|
|
||||||
|
// Decide direct vs indirect call
|
||||||
|
var isDirectFunc: bool = false;
|
||||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
n.strValue = expr.child1.strValue;
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
|
||||||
|
if sym.kind == skFunc {
|
||||||
|
isDirectFunc = true;
|
||||||
}
|
}
|
||||||
// Lower arguments from linked list
|
}
|
||||||
|
|
||||||
|
if isDirectFunc {
|
||||||
|
n.kind = hCall;
|
||||||
|
n.strValue = expr.child1.strValue;
|
||||||
|
// Lower arguments into child1/child2/extraData
|
||||||
var arg: *ExprList = expr.callArgs;
|
var arg: *ExprList = expr.callArgs;
|
||||||
var argIdx: int = 0;
|
var argIdx: int = 0;
|
||||||
while arg != null as *ExprList {
|
while arg != null as *ExprList {
|
||||||
@@ -231,14 +279,12 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
} else if argIdx == 1 {
|
} else if argIdx == 1 {
|
||||||
n.child2 = lowered;
|
n.child2 = lowered;
|
||||||
} else if argIdx == 2 {
|
} else if argIdx == 2 {
|
||||||
// Third argument — start linked list
|
|
||||||
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
||||||
firstExtra.node = lowered;
|
firstExtra.node = lowered;
|
||||||
firstExtra.next = null as *HirArgList;
|
firstExtra.next = null as *HirArgList;
|
||||||
n.extraData = firstExtra as *void;
|
n.extraData = firstExtra as *void;
|
||||||
n.extraCount = 1;
|
n.extraCount = 1;
|
||||||
} else {
|
} else {
|
||||||
// Additional args — append to linked list
|
|
||||||
var cur: *HirArgList = n.extraData as *HirArgList;
|
var cur: *HirArgList = n.extraData as *HirArgList;
|
||||||
while cur.next != null as *HirArgList {
|
while cur.next != null as *HirArgList {
|
||||||
cur = cur.next;
|
cur = cur.next;
|
||||||
@@ -252,6 +298,39 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
arg = arg.next;
|
arg = arg.next;
|
||||||
argIdx = argIdx + 1;
|
argIdx = argIdx + 1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
n.kind = hCallIndirect;
|
||||||
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
|
// Lower arguments into child2/child3/extraData (child1 is callee)
|
||||||
|
var arg: *ExprList = expr.callArgs;
|
||||||
|
var argIdx: int = 0;
|
||||||
|
while arg != null as *ExprList {
|
||||||
|
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
|
||||||
|
if argIdx == 0 {
|
||||||
|
n.child2 = lowered;
|
||||||
|
} else if argIdx == 1 {
|
||||||
|
n.child3 = lowered;
|
||||||
|
} else if argIdx == 2 {
|
||||||
|
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
||||||
|
firstExtra.node = lowered;
|
||||||
|
firstExtra.next = null as *HirArgList;
|
||||||
|
n.extraData = firstExtra as *void;
|
||||||
|
n.extraCount = 1;
|
||||||
|
} else {
|
||||||
|
var cur: *HirArgList = n.extraData as *HirArgList;
|
||||||
|
while cur.next != null as *HirArgList {
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
||||||
|
newNode.node = lowered;
|
||||||
|
newNode.next = null as *HirArgList;
|
||||||
|
cur.next = newNode;
|
||||||
|
n.extraCount = n.extraCount + 1;
|
||||||
|
}
|
||||||
|
arg = arg.next;
|
||||||
|
argIdx = argIdx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,8 +479,10 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
if stmt.refStmtType != null as *TypeExpr {
|
if stmt.refStmtType != null as *TypeExpr {
|
||||||
alloca.intValue = stmt.refStmtType.kind;
|
alloca.intValue = stmt.refStmtType.kind;
|
||||||
alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType);
|
alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType);
|
||||||
// For pointer types, construct "PointeeType*"
|
// For function types, build C function-pointer syntax
|
||||||
if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
|
if stmt.refStmtType.kind == tekFunc {
|
||||||
|
alloca.typeName = Lcx_BuildFuncTypeName(stmt.refStmtType);
|
||||||
|
} else if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
|
||||||
alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
|
alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
|
||||||
} else if !String_Eq(stmt.refStmtType.typeName, "") {
|
} else if !String_Eq(stmt.refStmtType.typeName, "") {
|
||||||
alloca.typeName = stmt.refStmtType.typeName;
|
alloca.typeName = stmt.refStmtType.typeName;
|
||||||
@@ -413,6 +494,7 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
sym.name = stmt.strValue;
|
sym.name = stmt.strValue;
|
||||||
sym.typeKind = alloca.typeKind;
|
sym.typeKind = alloca.typeKind;
|
||||||
sym.typeName = alloca.typeName;
|
sym.typeName = alloca.typeName;
|
||||||
|
sym.refType = stmt.refStmtType;
|
||||||
sym.isMutable = false;
|
sym.isMutable = false;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
sym.decl = null as *Decl;
|
sym.decl = null as *Decl;
|
||||||
@@ -609,8 +691,10 @@ func Lcx_LowerParam(out: *HirParam, p: *Param) {
|
|||||||
out.name = p.name;
|
out.name = p.name;
|
||||||
if p.refParamType != null as *TypeExpr {
|
if p.refParamType != null as *TypeExpr {
|
||||||
out.typeKind = Lcx_ResolveTypeKind(p.refParamType);
|
out.typeKind = Lcx_ResolveTypeKind(p.refParamType);
|
||||||
// Use typeName directly if set (parser may have constructed "String*")
|
// Function type: build C function-pointer syntax
|
||||||
if !String_Eq(p.refParamType.typeName, "") {
|
if p.refParamType.kind == tekFunc {
|
||||||
|
out.typeName = Lcx_BuildFuncTypeName(p.refParamType);
|
||||||
|
} else if !String_Eq(p.refParamType.typeName, "") {
|
||||||
out.typeName = p.refParamType.typeName;
|
out.typeName = p.refParamType.typeName;
|
||||||
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
||||||
out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
||||||
@@ -681,7 +765,9 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
|
|||||||
sym.name = p.name;
|
sym.name = p.name;
|
||||||
sym.typeKind = Lcx_ResolveTypeKind(p.refParamType);
|
sym.typeKind = Lcx_ResolveTypeKind(p.refParamType);
|
||||||
// Build typeName same as Lcx_LowerParam
|
// Build typeName same as Lcx_LowerParam
|
||||||
if !String_Eq(p.refParamType.typeName, "") {
|
if p.refParamType.kind == tekFunc {
|
||||||
|
sym.typeName = Lcx_BuildFuncTypeName(p.refParamType);
|
||||||
|
} else if !String_Eq(p.refParamType.typeName, "") {
|
||||||
sym.typeName = p.refParamType.typeName;
|
sym.typeName = p.refParamType.typeName;
|
||||||
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
||||||
sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
||||||
|
|||||||
@@ -173,6 +173,45 @@ func parserParseType(p: *Parser) -> *TypeExpr {
|
|||||||
return te;
|
return te;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func(Params) -> Ret
|
||||||
|
if kindTok == tkFunc {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
discard parserExpect(p, tkLParen, "expected '(' after 'func'");
|
||||||
|
var params: *TypeExprList = null as *TypeExprList;
|
||||||
|
var paramsTail: *TypeExprList = null as *TypeExprList;
|
||||||
|
var count: int = 0;
|
||||||
|
while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile {
|
||||||
|
let paramTe: *TypeExpr = parserParseType(p);
|
||||||
|
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
|
||||||
|
node.te = paramTe;
|
||||||
|
node.next = null as *TypeExprList;
|
||||||
|
if params == null as *TypeExprList {
|
||||||
|
params = node;
|
||||||
|
} else {
|
||||||
|
paramsTail.next = node;
|
||||||
|
}
|
||||||
|
paramsTail = node;
|
||||||
|
count = count + 1;
|
||||||
|
if parserCheck(p, tkComma) {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
discard parserExpect(p, tkRParen, "expected ')' after func params");
|
||||||
|
var ret: *TypeExpr = null as *TypeExpr;
|
||||||
|
if parserCheck(p, tkArrow) {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
ret = parserParseType(p);
|
||||||
|
}
|
||||||
|
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
|
te.kind = tekFunc;
|
||||||
|
te.line = line;
|
||||||
|
te.column = col;
|
||||||
|
te.funcParams = params;
|
||||||
|
te.funcRet = ret;
|
||||||
|
te.funcParamCount = count;
|
||||||
|
return te;
|
||||||
|
}
|
||||||
|
|
||||||
// name
|
// name
|
||||||
let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name");
|
let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name");
|
||||||
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
|
|||||||
+3
-2
@@ -16,6 +16,7 @@ struct Symbol {
|
|||||||
name: String;
|
name: String;
|
||||||
typeKind: int;
|
typeKind: int;
|
||||||
typeName: String;
|
typeName: String;
|
||||||
|
refType: *TypeExpr; // original type expression (for func types, etc.)
|
||||||
isMutable: bool;
|
isMutable: bool;
|
||||||
isPublic: bool;
|
isPublic: bool;
|
||||||
decl: *Decl; // associated declaration (for funcs, structs, enums)
|
decl: *Decl; // associated declaration (for funcs, structs, enums)
|
||||||
@@ -72,7 +73,7 @@ func Scope_Lookup(scope: *Scope, name: String) -> Symbol {
|
|||||||
}
|
}
|
||||||
cur = cur.parent;
|
cur = cur.parent;
|
||||||
}
|
}
|
||||||
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl };
|
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
|
|||||||
}
|
}
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl };
|
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+72
-4
@@ -46,11 +46,54 @@ func Sema_ZeroInitSymbol(sym: *Symbol) {
|
|||||||
sym.name = "";
|
sym.name = "";
|
||||||
sym.typeKind = 0;
|
sym.typeKind = 0;
|
||||||
sym.typeName = "";
|
sym.typeName = "";
|
||||||
|
sym.refType = null as *TypeExpr;
|
||||||
sym.isMutable = false;
|
sym.isMutable = false;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
sym.decl = null as *Decl;
|
sym.decl = null as *Decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Build a tekFunc TypeExpr from a function declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Sema_BuildFuncTypeExprFromDecl(decl: *Decl) -> *TypeExpr {
|
||||||
|
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
|
te.kind = tekFunc;
|
||||||
|
te.line = decl.line;
|
||||||
|
te.column = decl.column;
|
||||||
|
te.funcRet = decl.retType;
|
||||||
|
te.funcParamCount = decl.paramCount;
|
||||||
|
var head: *TypeExprList = null as *TypeExprList;
|
||||||
|
var tail: *TypeExprList = null as *TypeExprList;
|
||||||
|
var i: int = 0;
|
||||||
|
while i < decl.paramCount && i < 9 {
|
||||||
|
var p: Param;
|
||||||
|
if i == 0 { p = decl.param0; }
|
||||||
|
else if i == 1 { p = decl.param1; }
|
||||||
|
else if i == 2 { p = decl.param2; }
|
||||||
|
else if i == 3 { p = decl.param3; }
|
||||||
|
else if i == 4 { p = decl.param4; }
|
||||||
|
else if i == 5 { p = decl.param5; }
|
||||||
|
else if i == 6 { p = decl.param6; }
|
||||||
|
else if i == 7 { p = decl.param7; }
|
||||||
|
else { p = decl.param8; }
|
||||||
|
if p.refParamType != null as *TypeExpr {
|
||||||
|
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
|
||||||
|
node.te = p.refParamType;
|
||||||
|
node.next = null as *TypeExprList;
|
||||||
|
if head == null as *TypeExprList {
|
||||||
|
head = node;
|
||||||
|
} else {
|
||||||
|
tail.next = node;
|
||||||
|
}
|
||||||
|
tail = node;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
te.funcParams = head;
|
||||||
|
return te;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Type resolution from TypeExpr → Type constants
|
// Type resolution from TypeExpr → Type constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -63,6 +106,10 @@ func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
|
|||||||
return tyPointer;
|
return tyPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if te.kind == tekFunc {
|
||||||
|
return tyFunc;
|
||||||
|
}
|
||||||
|
|
||||||
if String_Eq(name, "void") { return tyVoid; }
|
if String_Eq(name, "void") { return tyVoid; }
|
||||||
if String_Eq(name, "bool") { return tyBool; }
|
if String_Eq(name, "bool") { return tyBool; }
|
||||||
if String_Eq(name, "bool8") { return tyBool8; }
|
if String_Eq(name, "bool8") { return tyBool8; }
|
||||||
@@ -271,7 +318,9 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
Sema_EmitError(sema, expr.line, expr.column, errMsg2);
|
Sema_EmitError(sema, expr.line, expr.column, errMsg2);
|
||||||
return tyUnknown;
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
if sym.typeName != null as String && !String_Eq(sym.typeName, "") {
|
if sym.refType != null as *TypeExpr {
|
||||||
|
expr.refType = sym.refType;
|
||||||
|
} else if sym.typeName != null as String && !String_Eq(sym.typeName, "") {
|
||||||
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
te.kind = tekNamed;
|
te.kind = tekNamed;
|
||||||
te.typeName = sym.typeName;
|
te.typeName = sym.typeName;
|
||||||
@@ -318,7 +367,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
let op: int = expr.intValue;
|
let op: int = expr.intValue;
|
||||||
if op == tkBang { return tyBool; }
|
if op == tkBang { return tyBool; }
|
||||||
if op == tkStar { return tyUnknown; } // dereference — resolve pointee
|
if op == tkStar { return tyUnknown; } // dereference — resolve pointee
|
||||||
if op == tkAmp { return tyPointer; }
|
if op == tkAmp {
|
||||||
|
if expr.child1.refType != null as *TypeExpr {
|
||||||
|
expr.refType = expr.child1.refType;
|
||||||
|
}
|
||||||
|
return tyPointer;
|
||||||
|
}
|
||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,14 +386,23 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
|
|
||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
discard Sema_CheckExpr(sema, expr.child1);
|
let calleeType: int = Sema_CheckExpr(sema, expr.child1);
|
||||||
Sema_ResolveCallArgs(sema, expr);
|
Sema_ResolveCallArgs(sema, expr);
|
||||||
var arg: *ExprList = expr.callArgs;
|
var arg: *ExprList = expr.callArgs;
|
||||||
while arg != null as *ExprList {
|
while arg != null as *ExprList {
|
||||||
discard Sema_CheckExpr(sema, arg.expr);
|
discard Sema_CheckExpr(sema, arg.expr);
|
||||||
arg = arg.next;
|
arg = arg.next;
|
||||||
}
|
}
|
||||||
// Try to resolve return type from function declaration
|
// Indirect call through function-typed value
|
||||||
|
if calleeType == tyFunc {
|
||||||
|
if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc {
|
||||||
|
if expr.child1.refType.funcRet != null as *TypeExpr {
|
||||||
|
return Sema_ResolveType(sema, expr.child1.refType.funcRet);
|
||||||
|
}
|
||||||
|
return tyVoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Direct call to named function
|
||||||
if expr.child1.kind == ekIdent {
|
if expr.child1.kind == ekIdent {
|
||||||
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue);
|
||||||
if sym.kind == skFunc {
|
if sym.kind == skFunc {
|
||||||
@@ -421,7 +484,9 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
sym.name = stmt.strValue;
|
sym.name = stmt.strValue;
|
||||||
sym.typeKind = initType;
|
sym.typeKind = initType;
|
||||||
sym.typeName = "";
|
sym.typeName = "";
|
||||||
|
sym.refType = null as *TypeExpr;
|
||||||
if stmt.refStmtType != null as *TypeExpr {
|
if stmt.refStmtType != null as *TypeExpr {
|
||||||
|
sym.refType = stmt.refStmtType;
|
||||||
if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
|
if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
|
||||||
sym.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
|
sym.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
|
||||||
} else {
|
} else {
|
||||||
@@ -560,6 +625,7 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
sym.kind = skFunc;
|
sym.kind = skFunc;
|
||||||
sym.name = decl.strValue;
|
sym.name = decl.strValue;
|
||||||
sym.typeKind = tyFunc;
|
sym.typeKind = tyFunc;
|
||||||
|
sym.refType = Sema_BuildFuncTypeExprFromDecl(decl);
|
||||||
sym.isPublic = decl.isPublic;
|
sym.isPublic = decl.isPublic;
|
||||||
sym.decl = decl;
|
sym.decl = decl;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
@@ -629,6 +695,7 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
if decl.constType != null as *TypeExpr {
|
if decl.constType != null as *TypeExpr {
|
||||||
sym.typeKind = Sema_ResolveType(sema, decl.constType);
|
sym.typeKind = Sema_ResolveType(sema, decl.constType);
|
||||||
sym.typeName = decl.constType.typeName;
|
sym.typeName = decl.constType.typeName;
|
||||||
|
sym.refType = decl.constType;
|
||||||
} else {
|
} else {
|
||||||
sym.typeKind = tyInt;
|
sym.typeKind = tyInt;
|
||||||
}
|
}
|
||||||
@@ -810,6 +877,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
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);
|
||||||
|
pSym.refType = p.refParamType;
|
||||||
if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
||||||
pSym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
pSym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ func Type_ToString(t: Type) -> String {
|
|||||||
if t.kind == tyNamed { return t.name; }
|
if t.kind == tyNamed { return t.name; }
|
||||||
if t.kind == tyTypeParam { return t.name; }
|
if t.kind == tyTypeParam { return t.name; }
|
||||||
if t.kind == tyPointer { return String_Concat("*", t.innerName1); }
|
if t.kind == tyPointer { return String_Concat("*", t.innerName1); }
|
||||||
|
if t.kind == tyFunc { return t.name; }
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user