feat: C++ 阶段 2 - 模板函数基础
- template <class T> 函数: 捕获定义, 调用时按首参类型推断实例化 - 实例化命名: max(3,5)->max_i, max(1.5,2.5)->max_d, bigger(p1,p2)->bigger_Point - 输出三段式布局: [声明/类] [模板实例化定义] [顶层函数] - 类类型模板参数自动加 struct 前缀 - 多参数模板/模板类: 干净跳过并注释 - 修复: Cls a(3), b(7) 多声明符带构造参数 - 输出改为内存缓冲 (8MB) 支持重排
这个提交包含在:
二进制
二进制文件未显示。
+344
-8
@@ -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 <class T> 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<class T> */
|
||||
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;
|
||||
|
||||
在新工单中引用
屏蔽一个用户