diff --git a/bin/pcc-cpp.exe b/bin/pcc-cpp.exe index 1dc73a7..8a1de53 100644 Binary files a/bin/pcc-cpp.exe and b/bin/pcc-cpp.exe differ diff --git a/tools/pcc-cpp.c b/tools/pcc-cpp.c index e5125dc..640e4e1 100644 --- a/tools/pcc-cpp.c +++ b/tools/pcc-cpp.c @@ -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,34 +901,43 @@ 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; } - while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}"))) { - if (g_tok.type == T_EOF) break; - if (g_tok.type == T_PUBLIC || g_tok.type == T_PRIVATE || - g_tok.type == T_PROTECTED) { - next_tok(); - if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) next_tok(); - continue; - } - if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) { next_tok(); continue; } - { - char tybuf[512] = ""; - char tmp[256]; - /* use gather_decl: type + name (stops at '(' or ';' or '}') */ - 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); - } else if (tmp[0]) { - /* data member(s): type name [more] ; */ - ClassInfo *cci = find_class(clsname); - emit(" %s %s", tybuf, tmp); + /* 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(); + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) next_tok(); + continue; + } + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) { next_tok(); continue; } + { + char tybuf[512] = ""; + char tmp[256]; + /* use gather_decl: type + name (stops at '(' or ';' or '}') */ + 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, + g_virt_pending); + } else if (tmp[0]) { + /* data member(s): type name [more] ; */ + ClassInfo *cci = find_class(clsname); + emit(" %s %s", tybuf, tmp); add_member(cci, tmp); while (!(g_tok.type == T_PUNCT && (!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}")))) { @@ -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);