feat: C++ 阶段 2 - 虚函数/多态 (vtable)
- virtual 成员: 类结构加 __vtbl 指针字段 - 每类发射 vtable typedef (owner) + 实例, 含实现原型保证声明顺序 - 构造器设置 __vtbl = &Cls_vtbl_inst (含派生类 __base.__vtbl) - 覆盖检测: virtual_impl 沿继承链找实现 - 指针变量: Cls* p = &d -> (struct Cls*)&d 转换 - p->virt() -> p->__vtbl->virt(p) 虚分发; 非虚 -> Cls_meth(p, ...) - 函数签名: 类参数 Cls* / Cls 加 struct 前缀并注册为指针/值变量 - parse_class 重构: 先收集字段, 后发射 struct (vtbl 字段依赖解析结果)
这个提交包含在:
二进制
二进制文件未显示。
+307
-11
@@ -35,6 +35,7 @@ static FILE *g_in, *g_out;
|
||||
static Token g_tok;
|
||||
static int g_line = 1;
|
||||
static int g_ns_open = 0; /* namespace block depth (flattened) */
|
||||
static int g_virt_pending = 0; /* next member decl is virtual */
|
||||
|
||||
/* ---------------- templates ---------------- */
|
||||
|
||||
@@ -55,11 +56,14 @@ static TplInst *g_tplinsts = NULL;
|
||||
static int g_capture = 0; /* capture emit() into g_cap */
|
||||
static char g_cap[4096];
|
||||
static int g_cap_len;
|
||||
static int g_emit_sect = 0; /* 0 = decl buffer, 1 = function buffer */
|
||||
static int g_emit_sect = 0; /* 0 = decl buffer, 1 = fn buffer,
|
||||
2 = class-fields buffer */
|
||||
static char g_outbuf[8 * 1024 * 1024]; /* declarations (classes, globals) */
|
||||
static int g_out_len;
|
||||
static char g_fn_buf[8 * 1024 * 1024]; /* top-level function bodies */
|
||||
static int g_fn_len;
|
||||
static char g_fields[16384]; /* class data-member fields (per class) */
|
||||
static int g_fields_len;
|
||||
static char g_tpl_defs[16384]; /* instantiated definitions (flushed at end) */
|
||||
static int g_tpl_defs_len;
|
||||
|
||||
@@ -70,6 +74,9 @@ typedef struct ClassInfo {
|
||||
int has_dtor;
|
||||
char members[64][64]; /* member names (data + functions) */
|
||||
int n_members;
|
||||
char virt[16][64]; /* virtual method names (own) */
|
||||
char virt_ret[16][64]; /* virtual method return types */
|
||||
int n_virt;
|
||||
struct ClassInfo *next;
|
||||
} ClassInfo;
|
||||
static ClassInfo *g_classes = NULL;
|
||||
@@ -167,6 +174,59 @@ static void add_class(const char *name)
|
||||
g_classes = c;
|
||||
}
|
||||
|
||||
/* is 'm' a virtual method of this class (own or inherited)? */
|
||||
static int is_virtual(ClassInfo *ci, const char *m)
|
||||
{
|
||||
int i;
|
||||
if (!ci) return 0;
|
||||
for (i = 0; i < ci->n_virt; i++)
|
||||
if (!strcmp(ci->virt[i], m)) return 1;
|
||||
if (ci->base[0]) {
|
||||
ClassInfo *b = find_class(ci->base);
|
||||
if (b) return is_virtual(b, m);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* does this class need a vtable pointer (own or inherited virtuals)? */
|
||||
static int class_has_vtbl(ClassInfo *ci)
|
||||
{
|
||||
if (!ci) return 0;
|
||||
if (ci->n_virt > 0) return 1;
|
||||
if (ci->base[0]) {
|
||||
ClassInfo *b = find_class(ci->base);
|
||||
if (b) return class_has_vtbl(b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* topmost class whose vtable type is used (the one introducing virtuals) */
|
||||
static const char *vtbl_owner(ClassInfo *ci)
|
||||
{
|
||||
if (!ci) return "";
|
||||
if (ci->base[0]) {
|
||||
ClassInfo *b = find_class(ci->base);
|
||||
if (b && class_has_vtbl(b)) return vtbl_owner(b);
|
||||
}
|
||||
return ci->name;
|
||||
}
|
||||
|
||||
/* which function implements virtual 'm' for class 'ci' */
|
||||
static void virtual_impl(ClassInfo *ci, const char *m, char *out, int outsz)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ci->n_members; i++)
|
||||
if (!strcmp(ci->members[i], m)) {
|
||||
snprintf(out, outsz, "%s_%s", ci->name, m);
|
||||
return;
|
||||
}
|
||||
if (ci->base[0]) {
|
||||
ClassInfo *b = find_class(ci->base);
|
||||
if (b) { virtual_impl(b, m, out, outsz); return; }
|
||||
}
|
||||
snprintf(out, outsz, "%s_%s", ci->name, m);
|
||||
}
|
||||
|
||||
/* ---------------- tokenizer ---------------- */
|
||||
|
||||
static Token g_peek; /* one-token lookahead buffer */
|
||||
@@ -388,6 +448,12 @@ static void emit(const char *fmt, ...)
|
||||
n = vsnprintf(g_cap + g_cap_len, sizeof g_cap - g_cap_len, fmt, ap);
|
||||
if (n > 0) g_cap_len += n;
|
||||
if (g_cap_len >= (int)sizeof g_cap - 1) g_cap_len = sizeof g_cap - 1;
|
||||
} else if (g_emit_sect == 2) {
|
||||
if (g_fields_len < (int)sizeof g_fields - 1) {
|
||||
n = vsnprintf(g_fields + g_fields_len,
|
||||
sizeof g_fields - g_fields_len, fmt, ap);
|
||||
if (n > 0) g_fields_len += n;
|
||||
}
|
||||
} else if (g_emit_sect == 1) {
|
||||
if (g_fn_len < (int)sizeof g_fn_buf - 1) {
|
||||
n = vsnprintf(g_fn_buf + g_fn_len,
|
||||
@@ -457,7 +523,8 @@ static void proto_emit(const char *fmt, ...)
|
||||
static void gather_decl(char *type, int typesz, char *name, int namesz);
|
||||
static int tpl_call_check(char varcls[][128], char varname[][64], int nvars);
|
||||
|
||||
static void parse_member_decl(const char *cls, const char *ret_type, const char *mname_in)
|
||||
static void parse_member_decl(const char *cls, const char *ret_type,
|
||||
const char *mname_in, int is_virtual)
|
||||
{
|
||||
char name[256];
|
||||
char mname[256];
|
||||
@@ -475,6 +542,20 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
|
||||
if (is_ctor) ci->has_ctor = 1;
|
||||
if (is_dtor) ci->has_dtor = 1;
|
||||
else add_member(ci, name);
|
||||
/* record virtual methods (name + trimmed return type) */
|
||||
if (is_virtual && !is_ctor && !is_dtor && ci->n_virt < 16) {
|
||||
char rtb[64];
|
||||
int rl;
|
||||
snprintf(rtb, sizeof rtb, "%s", ret_type);
|
||||
rl = (int)strlen(rtb);
|
||||
while (rl > 0 && (rtb[rl-1] == ' ' || rtb[rl-1] == '\t'))
|
||||
rtb[--rl] = 0;
|
||||
strncpy(ci->virt[ci->n_virt], name, 63);
|
||||
ci->virt[ci->n_virt][63] = 0;
|
||||
strncpy(ci->virt_ret[ci->n_virt], rtb[0] ? rtb : "int", 63);
|
||||
ci->virt_ret[ci->n_virt][63] = 0;
|
||||
ci->n_virt++;
|
||||
}
|
||||
}
|
||||
/* only advance if g_tok is the name (not already consumed) */
|
||||
if (!mname_in || !mname_in[0])
|
||||
@@ -583,6 +664,12 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
|
||||
if (bci && bci->has_ctor)
|
||||
proto_emit(" %s_ctor(&this->__base);", mci->base);
|
||||
}
|
||||
/* ctor: set the vtable pointer */
|
||||
if (is_ctor && class_has_vtbl(mci)) {
|
||||
if (mci->base[0])
|
||||
proto_emit(" this->__base.__vtbl = &%s_vtbl_inst;", cls);
|
||||
proto_emit(" this->__vtbl = &%s_vtbl_inst;", cls);
|
||||
}
|
||||
{
|
||||
int bd = 1;
|
||||
int prev_dot = 0;
|
||||
@@ -771,6 +858,21 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
|
||||
}
|
||||
}
|
||||
|
||||
/* emit a prototype for a mangled member function "Cls_method" */
|
||||
static void emit_impl_proto(const char *mname, const char *ret)
|
||||
{
|
||||
const char *us = strrchr(mname, '_');
|
||||
if (us) {
|
||||
int n = (int)(us - mname);
|
||||
if (n > 0 && n < 95) {
|
||||
char cls[96];
|
||||
memcpy(cls, mname, n);
|
||||
cls[n] = 0;
|
||||
emit(" %s %s(struct %s *this);\n", ret, mname, cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_class(void)
|
||||
{
|
||||
char clsname[128];
|
||||
@@ -799,15 +901,23 @@ static void parse_class(void)
|
||||
ci = find_class(clsname);
|
||||
if (base[0]) strncpy(ci->base, base, sizeof ci->base - 1);
|
||||
|
||||
emit("struct %s {\n", clsname);
|
||||
if (base[0])
|
||||
emit(" struct %s __base;\n", base);
|
||||
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) next_tok();
|
||||
else { emit("};\n\n"); return; }
|
||||
else { emit("struct %s;\n\n", clsname); return; }
|
||||
|
||||
/* phase 1: parse the class body; data members go to the fields
|
||||
buffer (g_emit_sect=2), member functions go to g_protos. The
|
||||
struct is emitted afterwards once virtuals are known. */
|
||||
{
|
||||
int was_sect = g_emit_sect;
|
||||
g_emit_sect = 2;
|
||||
g_fields_len = 0;
|
||||
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}"))) {
|
||||
if (g_tok.type == T_EOF) break;
|
||||
if (g_tok.type == T_VIRTUAL) {
|
||||
g_virt_pending = 1;
|
||||
next_tok();
|
||||
continue;
|
||||
}
|
||||
if (g_tok.type == T_PUBLIC || g_tok.type == T_PRIVATE ||
|
||||
g_tok.type == T_PROTECTED) {
|
||||
next_tok();
|
||||
@@ -822,7 +932,8 @@ static void parse_class(void)
|
||||
gather_decl(tybuf, sizeof tybuf, tmp, sizeof tmp);
|
||||
if (tmp[0] && g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
|
||||
/* member function decl */
|
||||
parse_member_decl(clsname, tybuf[0] ? tybuf : "int", tmp);
|
||||
parse_member_decl(clsname, tybuf[0] ? tybuf : "int", tmp,
|
||||
g_virt_pending);
|
||||
} else if (tmp[0]) {
|
||||
/* data member(s): type name [more] ; */
|
||||
ClassInfo *cci = find_class(clsname);
|
||||
@@ -861,10 +972,48 @@ static void parse_class(void)
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
|
||||
}
|
||||
}
|
||||
g_virt_pending = 0;
|
||||
}
|
||||
g_emit_sect = was_sect;
|
||||
}
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
|
||||
/* phase 2: emit the struct (vtbl field known once body is parsed) */
|
||||
emit("struct %s {\n", clsname);
|
||||
if (base[0])
|
||||
emit(" struct %s __base;\n", base);
|
||||
if (class_has_vtbl(ci))
|
||||
emit(" struct %s_vtbl *__vtbl;\n", vtbl_owner(ci));
|
||||
emit("%s", g_fields);
|
||||
emit("};\n");
|
||||
/* vtable: typedef once for the owner, instance per class */
|
||||
if (ci->n_virt > 0) {
|
||||
int i;
|
||||
emit("typedef struct %s_vtbl {\n", ci->name);
|
||||
for (i = 0; i < ci->n_virt; i++)
|
||||
emit(" %s (*%s)(struct %s *this);\n",
|
||||
ci->virt_ret[i], ci->virt[i], ci->name);
|
||||
emit("} %s_vtbl;\n", ci->name);
|
||||
}
|
||||
if (class_has_vtbl(ci)) {
|
||||
int i;
|
||||
const char *owner = vtbl_owner(ci);
|
||||
ClassInfo *oci = find_class(owner);
|
||||
/* prototypes for the implementations so the initializer is valid */
|
||||
for (i = 0; i < oci->n_virt; i++) {
|
||||
char impl[64];
|
||||
virtual_impl(ci, oci->virt[i], impl, sizeof impl);
|
||||
emit_impl_proto(impl, oci->virt_ret[i]);
|
||||
}
|
||||
emit("static %s_vtbl %s_vtbl_inst = {\n", owner, clsname);
|
||||
for (i = 0; i < oci->n_virt; i++) {
|
||||
char impl[64];
|
||||
virtual_impl(ci, oci->virt[i], impl, sizeof impl);
|
||||
emit(" (%s(*)(struct %s*))%s,\n",
|
||||
oci->virt_ret[i], owner, impl);
|
||||
}
|
||||
emit("};\n");
|
||||
}
|
||||
/* emit member function prototypes after the struct */
|
||||
if (g_proto_len) {
|
||||
emit("%s", g_protos);
|
||||
@@ -1008,6 +1157,12 @@ static void parse_member_definition(const char *ret_type)
|
||||
if (bci && bci->has_ctor)
|
||||
emit(" %s_ctor(&this->__base);\n", ci->base);
|
||||
}
|
||||
/* ctor: set the vtable pointer */
|
||||
if (is_ctor && class_has_vtbl(ci)) {
|
||||
if (ci->base[0])
|
||||
emit(" this->__base.__vtbl = &%s_vtbl_inst;\n", cls);
|
||||
emit(" this->__vtbl = &%s_vtbl_inst;\n", cls);
|
||||
}
|
||||
{
|
||||
int depth = 0;
|
||||
ClassInfo *mci = find_class(cls);
|
||||
@@ -1545,6 +1700,13 @@ static int tpl_call_check(char varcls[][128], char varname[][64], int nvars)
|
||||
|
||||
static void parse_function(const char *ret_type, const char *name)
|
||||
{
|
||||
char varcls[64][128]; /* variable name -> class name */
|
||||
char varname[64][64];
|
||||
int nvars = 0;
|
||||
char ptrcls[64][128]; /* pointer variable name -> class name */
|
||||
char ptrname[64][64];
|
||||
int nptrs = 0;
|
||||
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) {
|
||||
/* member function: class is 'name', method follows */
|
||||
g_tok.type = T_IDENT;
|
||||
@@ -1561,17 +1723,63 @@ static void parse_function(const char *ret_type, const char *name)
|
||||
return;
|
||||
}
|
||||
|
||||
/* class return type needs 'struct' prefix */
|
||||
{
|
||||
char rtbuf[160];
|
||||
int rl;
|
||||
snprintf(rtbuf, sizeof rtbuf, "%s", ret_type);
|
||||
rl = (int)strlen(rtbuf);
|
||||
while (rl > 0 && (rtbuf[rl-1] == ' ' || rtbuf[rl-1] == '\t'))
|
||||
rtbuf[--rl] = 0;
|
||||
if (find_class(rtbuf)) {
|
||||
static char rtbuf2[192];
|
||||
snprintf(rtbuf2, sizeof rtbuf2, "struct %s", rtbuf);
|
||||
ret_type = rtbuf2;
|
||||
}
|
||||
}
|
||||
|
||||
emit("%s %s", ret_type, name);
|
||||
/* signature: class types need 'struct'; class params are recorded */
|
||||
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{"))) {
|
||||
if (g_tok.type == T_EOF) break;
|
||||
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
|
||||
char clsname[128];
|
||||
Token nxt;
|
||||
strncpy(clsname, g_tok.text, sizeof clsname - 1);
|
||||
clsname[sizeof clsname - 1] = 0;
|
||||
read_tok(&nxt);
|
||||
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "*")) {
|
||||
/* Cls* name -> struct Cls* name (pointer param) */
|
||||
emit(" struct %s*", clsname);
|
||||
next_tok(); /* g_tok = param name */
|
||||
if (g_tok.type == T_IDENT && nptrs < 64) {
|
||||
strncpy(ptrcls[nptrs], clsname, 127);
|
||||
strncpy(ptrname[nptrs], g_tok.text, 63);
|
||||
nptrs++;
|
||||
emit(" %s", g_tok.text);
|
||||
next_tok();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (nxt.type == T_IDENT) {
|
||||
/* Cls name -> struct Cls name (value param) */
|
||||
emit(" struct %s %s", clsname, nxt.text);
|
||||
if (nvars < 64) {
|
||||
strncpy(varcls[nvars], clsname, 127);
|
||||
strncpy(varname[nvars], nxt.text, 63);
|
||||
nvars++;
|
||||
}
|
||||
next_tok();
|
||||
continue;
|
||||
}
|
||||
emit(" struct %s", clsname);
|
||||
unread_tok(&nxt);
|
||||
}
|
||||
emit_tok();
|
||||
next_tok();
|
||||
}
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
|
||||
int depth = 0;
|
||||
char varcls[64][128]; /* variable name -> class name */
|
||||
char varname[64][64];
|
||||
int nvars = 0;
|
||||
emit(" {");
|
||||
next_tok();
|
||||
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 0)) {
|
||||
@@ -1602,6 +1810,35 @@ static void parse_function(const char *ret_type, const char *name)
|
||||
clsname[sizeof clsname - 1] = 0;
|
||||
emit(" struct %s", clsname);
|
||||
next_tok();
|
||||
/* pointer declaration: Cls* p = ... */
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "*")) {
|
||||
emit(" *");
|
||||
next_tok();
|
||||
if (g_tok.type == T_IDENT && nptrs < 64) {
|
||||
strncpy(ptrcls[nptrs], clsname, 127);
|
||||
strncpy(ptrname[nptrs], g_tok.text, 63);
|
||||
nptrs++;
|
||||
emit(" %s", g_tok.text);
|
||||
next_tok();
|
||||
/* init: = &var -> = (struct Cls *)&var */
|
||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "=")) {
|
||||
Token amp, vv;
|
||||
read_tok(&);
|
||||
if (amp.type == T_PUNCT && !strcmp(amp.text, "&")) {
|
||||
read_tok(&vv);
|
||||
if (vv.type == T_IDENT) {
|
||||
emit(" = (struct %s *)&%s",
|
||||
clsname, vv.text);
|
||||
next_tok();
|
||||
continue;
|
||||
}
|
||||
unread_tok(&vv);
|
||||
}
|
||||
unread_tok(&);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* next token(s): var [= ...] */
|
||||
if (g_tok.type == T_IDENT && nvars < 64) {
|
||||
char vname[64];
|
||||
@@ -1838,6 +2075,65 @@ static void parse_function(const char *ret_type, const char *name)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* pointer member call / virtual dispatch: p->method(args) */
|
||||
if (g_tok.type == T_IDENT && nptrs > 0) {
|
||||
int pi;
|
||||
for (pi = 0; pi < nptrs; pi++) {
|
||||
if (!strcmp(g_tok.text, ptrname[pi])) {
|
||||
Token t1, t2;
|
||||
read_tok(&t1);
|
||||
if (t1.type == T_PUNCT && !strcmp(t1.text, "->")) {
|
||||
read_tok(&t2);
|
||||
if (t2.type == T_IDENT) {
|
||||
Token t3;
|
||||
read_tok(&t3);
|
||||
if (t3.type == T_PUNCT &&
|
||||
!strcmp(t3.text, "(")) {
|
||||
char meth[128];
|
||||
char mname[256];
|
||||
ClassInfo *pci = find_class(ptrcls[pi]);
|
||||
strncpy(meth, t2.text, sizeof meth - 1);
|
||||
meth[sizeof meth - 1] = 0;
|
||||
if (is_virtual(pci, meth)) {
|
||||
/* dispatch through the vtable */
|
||||
emit(" %s->__vtbl->%s(%s",
|
||||
g_tok.text, meth, g_tok.text);
|
||||
} else {
|
||||
snprintf(mname, sizeof mname, "%s_%s",
|
||||
ptrcls[pi], meth);
|
||||
emit(" %s(%s", mname, g_tok.text);
|
||||
}
|
||||
next_tok(); /* -> first arg or ')' */
|
||||
if (!(g_tok.type == T_PUNCT &&
|
||||
!strcmp(g_tok.text, ")"))) {
|
||||
emit(", ");
|
||||
int adepth = 0;
|
||||
while (!(g_tok.type == T_PUNCT &&
|
||||
!strcmp(g_tok.text, ")") &&
|
||||
adepth == 0)) {
|
||||
if (g_tok.type == T_EOF) break;
|
||||
if (g_tok.type == T_PUNCT &&
|
||||
!strcmp(g_tok.text, "(")) adepth++;
|
||||
if (g_tok.type == T_PUNCT &&
|
||||
!strcmp(g_tok.text, ")")) adepth--;
|
||||
emit_tok();
|
||||
next_tok();
|
||||
}
|
||||
}
|
||||
emit(")");
|
||||
if (g_tok.type == T_PUNCT &&
|
||||
!strcmp(g_tok.text, ")")) next_tok();
|
||||
goto next_body_tok;
|
||||
}
|
||||
unread_tok(&t3);
|
||||
}
|
||||
unread_tok(&t2);
|
||||
}
|
||||
unread_tok(&t1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* class name used as type -> add 'struct' */
|
||||
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
|
||||
emit(" struct %s", g_tok.text);
|
||||
|
||||
在新工单中引用
屏蔽一个用户