diff --git a/bin/pcc-cpp.exe b/bin/pcc-cpp.exe index 2b3a3d5..1dc73a7 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 5c14abb..e5125dc 100644 --- a/tools/pcc-cpp.c +++ b/tools/pcc-cpp.c @@ -36,6 +36,33 @@ static Token g_tok; static int g_line = 1; static int g_ns_open = 0; /* namespace block depth (flattened) */ +/* ---------------- templates ---------------- */ + +typedef struct TplFn { + char name[64]; /* template function name */ + char tparam[32]; /* type parameter name (e.g. "T") */ + char body[4096]; /* captured function text with T placeholder */ + struct TplFn *next; +} TplFn; +static TplFn *g_tplfns = NULL; + +typedef struct TplInst { + char name[128]; /* mangled instantiated name, e.g. max_i */ + struct TplInst *next; +} TplInst; +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 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_tpl_defs[16384]; /* instantiated definitions (flushed at end) */ +static int g_tpl_defs_len; + typedef struct ClassInfo { char name[128]; char base[128]; @@ -355,8 +382,23 @@ static int g_out_is_word = 0; /* last emitted char was word char */ static void emit(const char *fmt, ...) { va_list ap; + int n; va_start(ap, fmt); - vfprintf(g_out, fmt, ap); + if (g_capture) { + 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 == 1) { + if (g_fn_len < (int)sizeof g_fn_buf - 1) { + n = vsnprintf(g_fn_buf + g_fn_len, + sizeof g_fn_buf - g_fn_len, fmt, ap); + if (n > 0) g_fn_len += n; + } + } else if (g_out_len < (int)sizeof g_outbuf - 1) { + n = vsnprintf(g_outbuf + g_out_len, + sizeof g_outbuf - g_out_len, fmt, ap); + if (n > 0) g_out_len += n; + } va_end(ap); } @@ -413,6 +455,7 @@ static void proto_emit(const char *fmt, ...) /* ---------------- class parsing ---------------- */ 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) { @@ -555,6 +598,9 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char } unread_tok(&nxt); } + /* template function call inside a method body */ + if (tpl_call_check(NULL, NULL, 0)) + continue; if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { bd++; proto_emit(" {"); prev_dot = 0; next_tok(); continue; } if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) { bd--; if (bd > 0) proto_emit("}"); next_tok(); continue; } if (g_tok.type == T_THIS) { proto_emit("this"); prev_dot = 0; next_tok(); continue; } @@ -978,6 +1024,9 @@ static void parse_member_definition(const char *ret_type) } unread_tok(&nxt); } + /* template function call inside a method body */ + if (tpl_call_check(NULL, NULL, 0)) + continue; if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { depth++; emit(" {"); prev_dot = 0; next_tok(); continue; } if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) { if (depth > 0) { depth--; emit("}"); next_tok(); continue; } @@ -1258,6 +1307,9 @@ static void parse_free_operator(const char *ret_type, const char *name) } unread_tok(&nxt); } + /* template function call inside an operator body */ + if (tpl_call_check(NULL, NULL, 0)) + continue; if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { bdepth++; emit(" {"); @@ -1362,6 +1414,135 @@ static void parse_free_operator(const char *ret_type, const char *name) } } +/* ---------------- templates ---------------- */ + +/* replace standalone identifiers 'from' with 'to' in src */ +static void tpl_subst(char *dst, int dsz, const char *src, + const char *from, const char *to) +{ + int d = 0, i = 0, fl = (int)strlen(from), tl = (int)strlen(to); + while (src[i] && d < dsz - 1) { + if (!strncmp(src + i, from, fl) && + (i == 0 || !(isalnum((unsigned char)src[i-1]) || src[i-1] == '_')) && + !(isalnum((unsigned char)src[i+fl]) || src[i+fl] == '_')) { + if (d + tl < dsz - 1) { + memcpy(dst + d, to, tl); + d += tl; + } + i += fl; + } else { + dst[d++] = src[i++]; + } + } + dst[d] = 0; +} + +static TplFn *find_tplfn(const char *name) +{ + TplFn *p; + for (p = g_tplfns; p; p = p->next) + if (!strcmp(p->name, name)) return p; + return NULL; +} + +static const char *type_suffix(const char *t) +{ + if (!strcmp(t, "int")) return "i"; + if (!strcmp(t, "double")) return "d"; + if (!strcmp(t, "float")) return "f"; + if (!strcmp(t, "char")) return "c"; + if (!strcmp(t, "long")) return "l"; + if (!strcmp(t, "short")) return "s"; + if (!strcmp(t, "unsigned int")) return "u"; + return t; /* class name */ +} + +/* emit (once) the instantiated definition of a template function */ +static void instantiate_tpl_fn(TplFn *tf, const char *ttype, const char *mname) +{ + TplInst *p; + for (p = g_tplinsts; p; p = p->next) + if (!strcmp(p->name, mname)) return; + p = (TplInst*)calloc(1, sizeof *p); + strncpy(p->name, mname, sizeof p->name - 1); + p->name[sizeof p->name - 1] = 0; + p->next = g_tplinsts; + g_tplinsts = p; + { + char tmp[4096], def[4096]; + char stype[96]; + const char *subst_type = ttype; + if (find_class(ttype)) { + snprintf(stype, sizeof stype, "struct %s", ttype); + subst_type = stype; + } + tpl_subst(tmp, sizeof tmp, tf->body, tf->tparam, subst_type); + tpl_subst(def, sizeof def, tmp, tf->name, mname); + { + int l = (int)strlen(def); + if (g_tpl_defs_len + l + 1 < (int)sizeof g_tpl_defs) { + memcpy(g_tpl_defs + g_tpl_defs_len, def, l); + g_tpl_defs_len += l; + g_tpl_defs[g_tpl_defs_len++] = '\n'; + } + } + } +} + +/* If g_tok is a template function name followed by '(', infer the + concrete type from the first argument and rewrite g_tok to the + mangled instantiation name (the def is queued in g_tpl_defs). + Returns 1 if handled. */ +static int tpl_call_check(char varcls[][128], char varname[][64], int nvars) +{ + TplFn *tf; + Token nxt, a1; + const char *ttype = "int"; + char suffix[64]; + char mname[128]; + + if (g_tok.type != T_IDENT) return 0; + tf = find_tplfn(g_tok.text); + if (!tf) return 0; + read_tok(&nxt); + if (!(nxt.type == T_PUNCT && !strcmp(nxt.text, "("))) { + unread_tok(&nxt); + return 0; + } + /* stream is now at the first argument; read it to infer the type */ + read_tok(&a1); + if (a1.type == T_NUMBER) { + if (strchr(a1.text, '.') || strchr(a1.text, 'e') || + strchr(a1.text, 'E') || strchr(a1.text, 'f') || + strchr(a1.text, 'F') || strchr(a1.text, 'l')) + ttype = "double"; + else + ttype = "int"; + } else if (a1.type == T_CHAR) { + ttype = "char"; + } else if (a1.type == T_IDENT && nvars > 0) { + int k; + ttype = "int"; + for (k = 0; k < nvars; k++) + if (!strcmp(a1.text, varname[k])) { + ttype = varcls[k]; + break; + } + } + /* restore the stream: arg back, then '(' back */ + unread_tok(&a1); + unread_tok(&nxt); + /* g_tok is still the template name; rewrite it to the mangle */ + strncpy(suffix, type_suffix(ttype), sizeof suffix - 1); + suffix[sizeof suffix - 1] = 0; + snprintf(mname, sizeof mname, "%s_%s", tf->name, suffix); + instantiate_tpl_fn(tf, ttype, mname); + g_tok.type = T_IDENT; + strncpy(g_tok.text, mname, sizeof g_tok.text - 1); + g_tok.text[sizeof g_tok.text - 1] = 0; + return 1; +} + static void parse_function(const char *ret_type, const char *name) { if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) { @@ -1405,6 +1586,9 @@ static void parse_function(const char *ret_type, const char *name) } unread_tok(&nxt); } + /* template function call: max(a, b) -> max_i(a, b) */ + if (tpl_call_check(varcls, varname, nvars)) + continue; if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { depth++; emit(" {"); next_tok(); continue; } if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) { if (depth > 0) { depth--; emit("}"); next_tok(); continue; } @@ -1499,7 +1683,7 @@ static void parse_function(const char *ret_type, const char *name) if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) next_tok(); } - continue; + goto add_decls; } /* plain declaration: Cls var; -> default ctor */ if (g_tok.type == T_PUNCT && @@ -1510,16 +1694,53 @@ static void parse_function(const char *ret_type, const char *name) } } } - /* additional declarators: Cls a, b, c; */ + /* additional declarators: Cls a, b(2), c; */ + add_decls: while (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) { - emit(", "); next_tok(); if (g_tok.type == T_IDENT && nvars < 64) { + char vn[64]; strncpy(varcls[nvars], clsname, 127); strncpy(varname[nvars], g_tok.text, 63); + strncpy(vn, g_tok.text, sizeof vn - 1); + vn[sizeof vn - 1] = 0; nvars++; - emit(" %s", g_tok.text); next_tok(); + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) { + ClassInfo *cci2 = find_class(clsname); + if (cci2 && cci2->has_ctor) { + /* declarator with ctor args: a(3), b(7) */ + emit("; struct %s %s; %s_ctor(&%s", + clsname, vn, clsname, vn); + next_tok(); + if (g_tok.type == T_PUNCT && + !strcmp(g_tok.text, ")")) { + emit(")"); + next_tok(); + } else { + 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(); + } + continue; /* look for more declarators */ + } + emit(", %s(", vn); + continue; + } + emit(", %s", vn); } } continue; @@ -1727,6 +1948,100 @@ static void gather_decl(char *type, int typesz, char *name, int namesz) /* no name found; caller handles */ } +/* skip a declaration up to ';' or a balanced '}' */ +static void skip_decl(void) +{ + int depth = 0; + while (!(g_tok.type == T_EOF)) { + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) depth++; + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) { + if (depth == 0) { next_tok(); return; } + depth--; + } + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";") && depth == 0) { + next_tok(); + return; + } + next_tok(); + } +} + +/* template function/class */ +static void parse_template(void) +{ + char tparam[32] = "T"; + char tybuf[512], nmbuf[256]; + int multi = 0; + + next_tok(); /* '<' */ + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "<")) { + next_tok(); + /* class T | typename T | struct T */ + if (g_tok.type == T_CLASS || g_tok.type == T_STRUCT || + (g_tok.type == T_IDENT && !strcmp(g_tok.text, "typename"))) + next_tok(); + if (g_tok.type == T_IDENT) { + strncpy(tparam, g_tok.text, sizeof tparam - 1); + tparam[sizeof tparam - 1] = 0; + next_tok(); + } + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) multi = 1; + /* skip to '>' */ + while (!(g_tok.type == T_EOF)) { + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) break; + next_tok(); + } + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) next_tok(); + } else if (g_tok.type == T_CLASS) { + /* template */ + next_tok(); + if (g_tok.type == T_IDENT) { + strncpy(tparam, g_tok.text, sizeof tparam - 1); + tparam[sizeof tparam - 1] = 0; + next_tok(); + } + if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) next_tok(); + } + + if (multi) { + /* multi-parameter templates: not supported, skip declaration */ + skip_decl(); + emit("/* skipped multi-param template */\n"); + return; + } + + if (g_tok.type == T_CLASS || g_tok.type == T_STRUCT) { + /* template class: not yet supported, skip it cleanly */ + skip_decl(); + emit("/* skipped template class */\n"); + return; + } + + /* template function */ + gather_decl(tybuf, sizeof tybuf, nmbuf, sizeof nmbuf); + if (nmbuf[0]) { + g_capture = 1; + g_cap_len = 0; + parse_function(tybuf[0] ? tybuf : "int", nmbuf); + g_capture = 0; + g_cap[g_cap_len] = 0; + { + TplFn *tf = (TplFn*)calloc(1, sizeof *tf); + strncpy(tf->name, nmbuf, sizeof tf->name - 1); + tf->name[sizeof tf->name - 1] = 0; + strncpy(tf->tparam, tparam, sizeof tf->tparam - 1); + tf->tparam[sizeof tf->tparam - 1] = 0; + strncpy(tf->body, g_cap, sizeof tf->body - 1); + tf->body[sizeof tf->body - 1] = 0; + tf->next = g_tplfns; + g_tplfns = tf; + } + } else { + skip_decl(); + emit("/* skipped unknown template */\n"); + } +} + static void parse_program(void) { for (;;) { @@ -1811,9 +2126,14 @@ static void parse_program(void) continue; } - if (g_tok.type == T_USING || g_tok.type == T_TEMPLATE || - g_tok.type == T_INLINE || g_tok.type == T_CONSTEXPR || - g_tok.type == T_FRIEND || g_tok.type == T_VIRTUAL) { + if (g_tok.type == T_TEMPLATE) { + parse_template(); + continue; + } + + if (g_tok.type == T_USING || g_tok.type == T_INLINE || + g_tok.type == T_CONSTEXPR || g_tok.type == T_FRIEND || + g_tok.type == T_VIRTUAL) { next_tok(); while (!(g_tok.type == T_EOF)) { if (g_tok.type == T_PUNCT && @@ -1837,13 +2157,19 @@ static void parse_program(void) char tybuf[512], nmbuf[256]; gather_decl(tybuf, sizeof tybuf, nmbuf, sizeof nmbuf); if (nmbuf[0]) { + int was = g_emit_sect; + g_emit_sect = 1; parse_function(tybuf[0] ? tybuf : "int", nmbuf); + g_emit_sect = was; } else if (g_tok.type == T_EOF) { break; } else if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) { /* function without return type (C style) */ + int was = g_emit_sect; + g_emit_sect = 1; emit("int "); parse_function("int", "f"); + g_emit_sect = was; } else { /* global variable: type + rest */ if (tybuf[0]) emit("%s", tybuf); @@ -1883,6 +2209,16 @@ int main(int argc, char **argv) next_tok(); parse_program(); + /* layout: [declarations/classes] [template instantiations] + [top-level functions] */ + fwrite(g_outbuf, 1, g_out_len, g_out); + if (g_tpl_defs_len) { + fputc('\n', g_out); + fwrite(g_tpl_defs, 1, g_tpl_defs_len, g_out); + } + fputc('\n', g_out); + fwrite(g_fn_buf, 1, g_fn_len, g_out); + fclose(g_in); if (g_out != stdout) fclose(g_out); return 0;