文件
Paze AI ed918d046d stage2: reference support (refs)
- Builtin refs: int& -> int* in signature, (*a) deref in body
- Class refs: Cls& -> Cls* in signature, p.field -> p->field in body
- Call-site & injection via FnRef table (swap(x,y) -> swap(&x,&y))
- Member call ref args also get & injected
- Virtual dispatch for pointer-ref params preserved
- parse_member_decl: ptr param recording + dot-to-arrow conversion
- parse_function: iref body rewrite + fnref call-site rewrite
2026-08-16 19:49:36 +08:00

2882 行
115 KiB
C

/*
* pcc-cpp.c - C++ to C transpiler for PCC (stage 1)
*
* Converts a C++ subset (class, member functions, ctors/dtors,
* simple inheritance) into equivalent C code, which is then
* compiled by pcc.
*
* License: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
/* ---------------- tokenizer ---------------- */
typedef enum {
T_EOF, T_IDENT, T_NUMBER, T_STRING, T_CHAR,
T_CLASS, T_STRUCT, T_PUBLIC, T_PRIVATE, T_PROTECTED,
T_THIS, T_NEW, T_DELETE, T_NAMESPACE, T_USING, T_VIRTUAL,
T_INLINE, T_FRIEND, T_TEMPLATE, T_CONSTEXPR,
T_CPP_OPERATOR,
T_PREPROC, T_PUNCT
} TokType;
typedef struct {
TokType type;
char text[256];
long ival;
} Token;
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 */
/* ---------------- references ---------------- */
typedef struct FnRef {
char fn[64];
int arg[16];
int narg;
struct FnRef *next;
} FnRef;
static FnRef *g_fnrefs = NULL;
static FnRef *find_fnref(const char *fn)
{
FnRef *p;
for (p = g_fnrefs; p; p = p->next)
if (!strcmp(p->fn, fn)) return p;
return NULL;
}
static int fr_has_arg(const FnRef *fr, int ai)
{
int i;
for (i = 0; i < fr->narg; i++)
if (fr->arg[i] == ai) return 1;
return 0;
}
static void add_fnref(const char *fn, int ai)
{
FnRef *p = find_fnref(fn);
if (!p) {
p = (FnRef*)calloc(1, sizeof *p);
strncpy(p->fn, fn, sizeof p->fn - 1);
p->fn[sizeof p->fn - 1] = 0;
p->next = g_fnrefs;
g_fnrefs = p;
}
if (p->narg < 16) p->arg[p->narg++] = ai;
}
/* ---------------- 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 = 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;
typedef struct ClassInfo {
char name[128];
char base[128];
int has_ctor;
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;
static ClassInfo *find_class(const char *name)
{
ClassInfo *c;
for (c = g_classes; c; c = c->next)
if (strcmp(c->name, name) == 0)
return c;
return NULL;
}
static void add_member(ClassInfo *ci, const char *m)
{
int i;
if (!ci) return;
for (i = 0; i < ci->n_members; i++)
if (!strcmp(ci->members[i], m)) return;
if (ci->n_members < 64)
strncpy(ci->members[ci->n_members++], m, 63);
}
static int is_member(ClassInfo *ci, const char *m)
{
int i;
if (!ci) return 0;
for (i = 0; i < ci->n_members; i++)
if (!strcmp(ci->members[i], m)) return 1;
/* search base class */
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b) return is_member(b, m);
}
return 0;
}
/* map an operator name (e.g. "operator+") to a mangled suffix */
static void mangle_operator(const char *method, char *out, int outsz)
{
/* "operator+" -> "_add", "operator-" -> "_sub", etc */
static const struct { const char *op; const char *name; } ops[] = {
{ "operator+", "_add" }, { "operator-", "_sub" },
{ "operator*", "_mul" }, { "operator/", "_div" },
{ "operator%", "_mod" }, { "operator==", "_eq" },
{ "operator!=", "_ne" }, { "operator<", "_lt" },
{ "operator>", "_gt" }, { "operator<=", "_le" },
{ "operator>=", "_ge" }, { "operator&&", "_and" },
{ "operator||", "_or" }, { "operator!", "_not" },
{ "operator&", "_bitand" }, { "operator|", "_bitor" },
{ "operator^", "_xor" }, { "operator~", "_inv" },
{ "operator<<", "_shl" }, { "operator>>", "_shr" },
{ "operator++", "_inc" }, { "operator--", "_dec" },
{ "operator=", "_assign" }, { "operator+=", "_add_assign" },
{ "operator-=", "_sub_assign" }, { "operator*=", "_mul_assign" },
{ "operator[]", "_index" }, { "operator()", "_call" },
{ "operator->", "_arrow" },
};
int i;
for (i = 0; i < (int)(sizeof ops / sizeof ops[0]); i++)
if (!strcmp(method, ops[i].op)) {
snprintf(out, outsz, "%s", ops[i].name);
return;
}
snprintf(out, outsz, "_op");
}
/* find access path for a member: base-class member -> this->__base */
static void member_path(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, "this");
return;
}
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b && is_member(b, m)) {
snprintf(out, outsz, "this->__base");
return;
}
}
snprintf(out, outsz, "this");
}
static void add_class(const char *name)
{
ClassInfo *c;
if (find_class(name)) return;
c = (ClassInfo*)calloc(1, sizeof *c);
strncpy(c->name, name, sizeof c->name - 1);
c->next = g_classes;
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 */
static int g_have_peek = 0;
static Token g_pb[16]; /* pushback stack for peeking */
static int g_pb_len = 0;
static void next_tok_internal(Token *t); /* fwd decl */
/* read next token (from pushback, peek buffer, or input) */
static void read_tok(Token *t)
{
if (g_pb_len > 0) {
*t = g_pb[--g_pb_len];
return;
}
if (g_have_peek) {
*t = g_peek;
g_have_peek = 0;
} else {
next_tok_internal(t);
}
}
/* push a token back onto the stack (LIFO) */
static void unread_tok(const Token *t)
{
if (g_pb_len < 16)
g_pb[g_pb_len++] = *t;
else {
/* fallback: single peek */
g_peek = *t;
g_have_peek = 1;
}
}
static void next_tok(void)
{
read_tok(&g_tok);
}
static int getc2(void)
{
int c = fgetc(g_in);
if (c == '\n') g_line++;
return c;
}
static void ungetc2(int c)
{
if (c == '\n') g_line--;
ungetc(c, g_in);
}
static TokType kw_type(const char *s)
{
if (!strcmp(s, "class")) return T_CLASS;
if (!strcmp(s, "struct")) return T_STRUCT;
if (!strcmp(s, "public")) return T_PUBLIC;
if (!strcmp(s, "private")) return T_PRIVATE;
if (!strcmp(s, "protected")) return T_PROTECTED;
if (!strcmp(s, "this")) return T_THIS;
if (!strcmp(s, "new")) return T_NEW;
if (!strcmp(s, "delete")) return T_DELETE;
if (!strcmp(s, "namespace")) return T_NAMESPACE;
if (!strcmp(s, "using")) return T_USING;
if (!strcmp(s, "virtual")) return T_VIRTUAL;
if (!strcmp(s, "inline")) return T_INLINE;
if (!strcmp(s, "friend")) return T_FRIEND;
if (!strcmp(s, "template")) return T_TEMPLATE;
if (!strcmp(s, "constexpr")) return T_CONSTEXPR;
if (!strcmp(s, "operator")) return T_CPP_OPERATOR;
return T_IDENT;
}
/* read a raw token from the input stream */
static void next_tok_internal(Token *t)
{
int c;
for (;;) {
c = getc2();
if (c == EOF) { t->type = T_EOF; t->text[0] = 0; return; }
if (isspace(c)) continue;
/* skip UTF-8 BOM (EF BB BF) at start of file */
if (c == 0xEF) {
int c1 = getc2(), c2 = getc2();
if (c1 == 0xBB && c2 == 0xBF) continue;
ungetc2(c2);
ungetc2(c1);
break;
}
if (c == '/') {
int c2 = getc2();
if (c2 == '/') {
while ((c = getc2()) != '\n' && c != EOF);
continue;
} else if (c2 == '*') {
int prev = 0;
while ((c = getc2()) != EOF) {
if (prev == '*' && c == '/') break;
prev = c;
}
continue;
} else {
ungetc2(c2);
t->type = T_PUNCT;
t->text[0] = '/'; t->text[1] = 0;
return;
}
}
break;
}
/* preprocessor directive: copy the whole line verbatim */
if (c == '#') {
int n = 0;
g_tok.type = T_PREPROC;
g_tok.text[n++] = '#';
while (n < 250) {
c = getc2();
if (c == '\n' || c == EOF) break;
g_tok.text[n++] = (char)c;
}
g_tok.text[n] = 0;
return;
}
if (isalpha(c) || c == '_') {
int n = 0;
t->text[n++] = (char)c;
while (n < 255) {
c = getc2();
if (isalnum(c) || c == '_')
t->text[n++] = (char)c;
else { ungetc2(c); break; }
}
t->text[n] = 0;
t->type = kw_type(t->text);
return;
}
if (isdigit(c)) {
int n = 0;
t->text[n++] = (char)c;
while (n < 255) {
c = getc2();
if (isalnum(c) || c == '.' || c == '_')
t->text[n++] = (char)c;
else { ungetc2(c); break; }
}
t->text[n] = 0;
t->ival = strtol(t->text, NULL, 0);
t->type = T_NUMBER;
return;
}
if (c == '"') {
int n = 0;
t->type = T_STRING;
t->text[n++] = '"';
while (n < 250) {
c = getc2();
t->text[n++] = (char)c;
if (c == '\\') {
t->text[n++] = (char)getc2();
} else if (c == '"') break;
else if (c == EOF || c == '\n') break;
}
t->text[n] = 0;
return;
}
if (c == '\'') {
int n = 0;
t->type = T_CHAR;
t->text[n++] = '\'';
while (n < 250) {
c = getc2();
t->text[n++] = (char)c;
if (c == '\\') {
t->text[n++] = (char)getc2();
} else if (c == '\'') break;
else if (c == EOF || c == '\n') break;
}
t->text[n] = 0;
return;
}
{
int c2 = getc2();
char two[3] = { (char)c, (char)c2, 0 };
static const char *multi[] = { "::", "->", "<<", ">>", "<=", ">=", "==",
"!=", "&&", "||", "++", "--", "+=", "-=",
"*=", "/=", "%=", "&=", "|=", "^=" };
int i;
for (i = 0; i < (int)(sizeof multi / sizeof multi[0]); i++)
if (!strcmp(two, multi[i])) {
strcpy(t->text, two);
t->type = T_PUNCT;
return;
}
ungetc2(c2);
t->text[0] = (char)c;
t->text[1] = 0;
t->type = T_PUNCT;
}
}
/* ---------------- output with smart spacing ---------------- */
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);
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 == 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,
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);
}
/* emit a token with proper spacing */
static int g_need_space = 0; /* whether to add space before next word */
static void emit_tok(void)
{
const char *t = g_tok.text;
int c;
if (g_tok.type == T_PREPROC) {
emit("\n%s\n", t); /* preprocessor line verbatim */
g_need_space = 1;
return;
}
if (g_tok.type == T_PUNCT) {
c = t[0];
if (c == ')' || c == ']' || c == ';' || c == ',' || c == '}' ||
c == '.' || c == ':' || c == '(' )
emit("%s", t);
else if (g_need_space) {
emit(" %s", t);
} else {
emit("%s", t);
}
/* after '.', '->', '::' no space before next word */
g_need_space = !(c == '.' || t[1] == '>' ||
(c == ':' && t[1] == ':'));
} else {
if (g_need_space)
emit(" %s", t);
else
emit("%s", t);
g_need_space = 1;
}
}
/* pending member function prototypes (emitted after struct) */
static char g_protos[8192];
static int g_proto_len;
static char g_params[1024]; /* captured params for inline defs */
static void proto_emit(const char *fmt, ...)
{
va_list ap;
int n;
va_start(ap, fmt);
n = vsnprintf(g_protos + g_proto_len, sizeof g_protos - g_proto_len, fmt, ap);
va_end(ap);
if (n > 0) g_proto_len += n;
if (g_proto_len >= (int)sizeof g_protos - 1) g_proto_len = sizeof g_protos - 1;
}
/* ---------------- 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, int is_virt)
{
char name[256];
char mname[256];
int is_ctor, is_dtor;
char bptrcls[64][128], bptrname[64][64];
int bnptrs = 0;
char biref[64][64];
int bn_iref = 0;
int sig_pidx = 0;
ClassInfo *ci = find_class(cls);
if (mname_in && mname_in[0])
strncpy(name, mname_in, sizeof name - 1);
else
strncpy(name, g_tok.text, sizeof name - 1);
name[sizeof name - 1] = 0;
is_ctor = !strcmp(name, cls);
is_dtor = (name[0] == '~' && !strcmp(name + 1, cls));
if (ci) {
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_virt && !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])
next_tok();
if (is_ctor)
snprintf(mname, sizeof mname, "%s_ctor", cls);
else if (is_dtor)
snprintf(mname, sizeof mname, "%s_dtor", cls);
else if (!strncmp(name, "operator", 8)) {
char suf[32];
mangle_operator(name, suf, sizeof suf);
snprintf(mname, sizeof mname, "%s%s", cls, suf);
} else
snprintf(mname, sizeof mname, "%s_%s", cls, name);
/* constructors/destructors return void */
if (is_ctor || is_dtor)
ret_type = "void";
/* class return type needs 'struct' prefix */
{
char rtbuf[128];
snprintf(rtbuf, sizeof rtbuf, "%s", ret_type);
/* trim trailing space */
{
int rl = (int)strlen(rtbuf);
while (rl > 0 && (rtbuf[rl-1] == ' ' || rtbuf[rl-1] == '\t'))
rtbuf[--rl] = 0;
}
if (find_class(rtbuf)) {
static char rtbuf2[160];
snprintf(rtbuf2, sizeof rtbuf2, "struct %s", rtbuf);
ret_type = rtbuf2;
}
}
proto_emit(" %s %s(struct %s *this", ret_type, mname, cls);
g_params[0] = 0;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
proto_emit(");\n");
next_tok();
} else {
proto_emit(", ");
int depth = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")") && depth == 0)
break;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) depth++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) depth--;
/* class type param -> struct Class */
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
char ptcls[128];
strncpy(ptcls, g_tok.text, sizeof ptcls - 1);
ptcls[sizeof ptcls - 1] = 0;
proto_emit(" struct %s", g_tok.text);
strncat(g_params, " struct ", sizeof g_params - strlen(g_params) - 1);
strncat(g_params, g_tok.text, sizeof g_params - strlen(g_params) - 1);
next_tok();
/* handle * or & suffix: convert to pointer param */
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, "*") || !strcmp(g_tok.text, "&"))) {
int is_ref = !strcmp(g_tok.text, "&");
proto_emit(" *");
strncat(g_params, " *", sizeof g_params - strlen(g_params) - 1);
next_tok();
/* record ptr param for body dispatch */
if (g_tok.type == T_IDENT && bnptrs < 64) {
strncpy(bptrcls[bnptrs], ptcls, 127);
strncpy(bptrname[bnptrs], g_tok.text, 63);
bnptrs++;
}
if (is_ref)
add_fnref(mname, sig_pidx);
}
continue;
}
/* smart spacing for prototype params */
{
const char *tt = g_tok.text;
int c = tt[0];
if (g_tok.type == T_PUNCT) {
if (c == '&') {
/* reference param -> pointer param */
proto_emit("*");
strncat(g_params, " *", sizeof g_params - strlen(g_params) - 1);
add_fnref(mname, sig_pidx);
} else {
if (!(c == ')' || c == ']' || c == ';' || c == ',' ||
c == '*' || c == '&' || c == '(' ))
proto_emit(" ");
proto_emit("%s", tt);
strncat(g_params, " ", sizeof g_params - strlen(g_params) - 1);
strncat(g_params, tt, sizeof g_params - strlen(g_params) - 1);
}
} else {
proto_emit(" %s", tt);
strncat(g_params, " ", sizeof g_params - strlen(g_params) - 1);
strncat(g_params, tt, sizeof g_params - strlen(g_params) - 1);
}
}
next_tok();
}
proto_emit(");\n");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) next_tok();
}
}
/* skip constructor initializer list: ": Base(args, ...)" */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) {
int bd = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) bd++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) bd--;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{") && bd == 0)
break;
next_tok();
}
}
/* after the prototype, either ';' (decl only) or '{' (inline body) */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
/* inline member function definition: emit the body as a
separate function after the class */
ClassInfo *mci = find_class(cls);
proto_emit("%s %s(struct %s *this%s", ret_type, mname, cls,
g_params[0] ? ", " : "");
proto_emit("%s) {\n", g_params);
next_tok(); /* consume '{' */
/* ctor: chain the base-class ctor (default) */
if (is_ctor && mci && mci->base[0]) {
ClassInfo *bci = find_class(mci->base);
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;
while (bd > 0) {
if (g_tok.type == T_EOF) break;
/* builtin ref param: a -> (*a) */
if (g_tok.type == T_IDENT && bn_iref > 0) {
int i;
for (i = 0; i < bn_iref; i++)
if (!strcmp(g_tok.text, biref[i])) break;
if (i < bn_iref) {
proto_emit(" (*%s)", g_tok.text);
next_tok();
continue;
}
}
/* namespace-qualified name: ns::name -> name */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "::")) {
next_tok(); /* consume '::' */
continue;
}
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; }
/* bare member name -> this->member (not after . or ->) */
if (g_tok.type == T_IDENT && !prev_dot && mci && is_member(mci, g_tok.text)
&& !find_class(g_tok.text)) {
char path[64];
member_path(mci, g_tok.text, path, sizeof path);
if (!strcmp(path, "this"))
proto_emit(" this->%s", g_tok.text);
else
proto_emit(" %s.%s", path, g_tok.text);
prev_dot = 0;
next_tok();
continue;
}
/* return ClassName(args) -> temp + ctor + return */
if (g_tok.type == T_IDENT && !strcmp(g_tok.text, "return")) {
Token t1;
read_tok(&t1);
if (t1.type == T_IDENT && find_class(t1.text)) {
Token t2;
char rcls[128];
strncpy(rcls, t1.text, sizeof rcls - 1);
rcls[sizeof rcls - 1] = 0;
read_tok(&t2);
if (t2.type == T_PUNCT && !strcmp(t2.text, "(")) {
/* return Cls(args) */
proto_emit(" return (struct %s)", rcls);
/* copy args as a compound literal: (struct Cls){args} */
proto_emit(" {");
next_tok(); /* consume '(' */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
proto_emit("}");
next_tok();
} else {
int rdepth = 0;
int rd_prev_dot = 0;
while (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")") && rdepth == 0)) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, "(")) rdepth++;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) rdepth--;
if (g_tok.type == T_IDENT && mci &&
is_member(mci, g_tok.text) &&
!find_class(g_tok.text) &&
!rd_prev_dot) {
char path[64];
member_path(mci, g_tok.text, path, sizeof path);
if (!strcmp(path, "this"))
proto_emit(" this->%s", g_tok.text);
else
proto_emit(" %s.%s", path, g_tok.text);
} else {
proto_emit(" %s", g_tok.text);
}
rd_prev_dot = (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ".") ||
!strcmp(g_tok.text, "->")));
next_tok();
}
proto_emit("}");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
proto_emit(";");
prev_dot = 0;
continue;
}
unread_tok(&t2);
}
unread_tok(&t1);
}
/* local class var: ClassType var(args) or ClassType var; */
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
char lcls[128];
Token n1, n2;
strncpy(lcls, g_tok.text, sizeof lcls - 1);
lcls[sizeof lcls - 1] = 0;
read_tok(&n1);
if (n1.type == T_IDENT) {
read_tok(&n2);
if (n2.type == T_PUNCT && !strcmp(n2.text, "(")) {
/* ctor call: emit decl + ctor call */
proto_emit(" struct %s %s; %s_ctor(&%s",
lcls, n1.text, lcls, n1.text);
unread_tok(&n2); /* restore '(' */
next_tok(); /* g_tok = '(' */
next_tok(); /* -> first arg or ')' */
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) {
proto_emit(")");
next_tok();
} else {
proto_emit(", ");
int adepth = 0;
int ad_prev_dot = 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--;
if (g_tok.type == T_IDENT && mci &&
is_member(mci, g_tok.text) &&
!find_class(g_tok.text) &&
!ad_prev_dot) {
char path[64];
member_path(mci, g_tok.text,
path, sizeof path);
if (!strcmp(path, "this"))
proto_emit(" this->%s", g_tok.text);
else
proto_emit(" %s.%s", path,
g_tok.text);
} else {
proto_emit(" %s", g_tok.text);
}
ad_prev_dot =
(g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ".") ||
!strcmp(g_tok.text, "->")));
next_tok();
}
proto_emit(")");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
prev_dot = 0;
continue;
}
/* not a ctor call: restore both tokens */
unread_tok(&n2);
unread_tok(&n1);
} else {
unread_tok(&n1);
}
/* plain class name in body: emit with 'struct' prefix */
prev_dot = 0;
if (find_class(g_tok.text))
proto_emit(" struct %s", g_tok.text);
else
proto_emit(" %s", g_tok.text);
next_tok();
continue;
}
/* ptr/var member dispatch: p->method / p.field */
if (g_tok.type == T_IDENT && bnptrs > 0) {
int pi;
for (pi = 0; pi < bnptrs; pi++) {
if (!strcmp(g_tok.text, bptrname[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], mname[256];
ClassInfo *pci = find_class(bptrcls[pi]);
strncpy(meth, t2.text, sizeof meth - 1);
meth[sizeof meth - 1] = 0;
if (is_virtual(pci, meth)) {
proto_emit(" %s->__vtbl->%s(%s",
g_tok.text, meth, g_tok.text);
} else {
snprintf(mname, sizeof mname, "%s_%s",
bptrcls[pi], meth);
proto_emit(" %s(%s", mname, g_tok.text);
}
next_tok(); next_tok(); next_tok();
if (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")"))) {
proto_emit(", ");
int ad = 0;
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")") && ad == 0)) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) ad++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) ad--;
proto_emit(" %s", g_tok.text);
next_tok();
}
}
proto_emit(")");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) next_tok();
prev_dot = 0;
continue;
}
unread_tok(&t3);
}
unread_tok(&t2);
} else if (t1.type == T_PUNCT && !strcmp(t1.text, ".")) {
unread_tok(&t1);
break;
}
unread_tok(&t1);
break;
}
}
}
/* fall-through: emit token as-is */
/* ptr ref: p.field -> p->field (fix dot to arrow for known ptr vars) */
if (g_tok.type == T_IDENT && bnptrs > 0) {
int pi2;
for (pi2 = 0; pi2 < bnptrs; pi2++) {
if (!strcmp(g_tok.text, bptrname[pi2])) {
Token nxt2;
read_tok(&nxt2);
if (nxt2.type == T_PUNCT && !strcmp(nxt2.text, ".")) {
Token nxt3;
read_tok(&nxt3);
if (nxt3.type == T_IDENT) {
proto_emit("%s->%s", g_tok.text, nxt3.text);
/* consumed '.', field by read_tok; skip 'p' via next_tok */
next_tok(); /* consume 'p' from g_tok */
prev_dot = 1;
continue;
}
unread_tok(&nxt3);
}
unread_tok(&nxt2);
break;
}
}
}
prev_dot = (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ".") || !strcmp(g_tok.text, "->")));
proto_emit(" %s", g_tok.text);
next_tok();
}
}
proto_emit("}\n\n");
/* skip trailing ; */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
} else {
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}"))) break;
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
}
}
/* 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];
char base[128] = "";
ClassInfo *ci;
next_tok();
if (g_tok.type != T_IDENT) { emit("/* anon class */\n"); return; }
strncpy(clsname, g_tok.text, sizeof clsname - 1);
clsname[sizeof clsname - 1] = 0;
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) {
next_tok();
if (g_tok.type == T_PUBLIC || g_tok.type == T_PRIVATE ||
g_tok.type == T_PROTECTED || g_tok.type == T_VIRTUAL)
next_tok();
if (g_tok.type == T_IDENT) {
strncpy(base, g_tok.text, sizeof base - 1);
base[sizeof base - 1] = 0;
next_tok();
}
}
add_class(clsname);
ci = find_class(clsname);
if (base[0]) strncpy(ci->base, base, sizeof ci->base - 1);
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) next_tok();
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();
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, "}")))) {
if (g_tok.type == T_EOF) break;
/* additional declarators: , y */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) {
emit(", ");
next_tok();
/* next word is another member name */
if (g_tok.type == T_IDENT) {
add_member(cci, g_tok.text);
}
continue;
}
emit_tok();
next_tok();
}
emit(";\n");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
} else if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
/* nothing - end of class */
} else {
/* unknown, copy through */
if (tybuf[0]) emit("%s", tybuf);
while (!(g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}")))) {
if (g_tok.type == T_EOF) break;
emit_tok();
next_tok();
}
emit(";\n");
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);
g_proto_len = 0;
}
emit("\n");
}
/* member function definition: Ret Class::method(params) { body } */
static void parse_member_definition(const char *ret_type)
{
char cls[128], method[128], mname[256];
int is_ctor = 0, is_dtor = 0;
ClassInfo *ci;
/* skip namespace qualifiers: math::Point::draw -> Point::draw.
gather_decl already consumed the first '::', so the stream is at
the second segment; if the first segment is not a class it is a
namespace prefix that must be dropped. */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
/* skip the first namespace segment (its '::' already consumed) */
next_tok(); /* g_tok = second segment */
/* skip remaining 'ns ::' pairs */
while (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "::")) {
next_tok(); /* g_tok = segment after '::' */
continue;
}
unread_tok(&nxt);
break;
}
}
strncpy(cls, g_tok.text, sizeof cls - 1);
cls[sizeof cls - 1] = 0;
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) next_tok();
if (g_tok.type == T_CPP_OPERATOR) {
/* operator overload definition: Class::operator+ */
char opname[64] = "operator";
next_tok();
if (g_tok.type == T_PUNCT) {
strncat(opname, g_tok.text, sizeof opname - strlen(opname) - 1);
next_tok();
} else if (g_tok.type == T_IDENT) {
strncat(opname, g_tok.text, sizeof opname - strlen(opname) - 1);
next_tok();
}
strncpy(method, opname, sizeof method - 1);
method[sizeof method - 1] = 0;
} else if (g_tok.type != T_IDENT) {
emit("/* bad member def */\n");
return;
} else {
strncpy(method, g_tok.text, sizeof method - 1);
method[sizeof method - 1] = 0;
next_tok();
}
if (!strcmp(method, cls)) is_ctor = 1;
if (method[0] == '~') is_dtor = 1;
if (is_ctor)
snprintf(mname, sizeof mname, "%s_ctor", cls);
else if (is_dtor)
snprintf(mname, sizeof mname, "%s_dtor", cls);
else if (!strncmp(method, "operator", 8)) {
char suf[32];
mangle_operator(method, suf, sizeof suf);
snprintf(mname, sizeof mname, "%s%s", cls, suf);
} else
snprintf(mname, sizeof mname, "%s_%s", cls, method);
ci = find_class(cls);
if (ci) { if (is_ctor) ci->has_ctor = 1; if (is_dtor) ci->has_dtor = 1; }
/* constructors/destructors return void */
if (is_ctor || is_dtor)
ret_type = "void";
/* class return type needs 'struct' prefix */
{
char rtbuf[160];
snprintf(rtbuf, sizeof rtbuf, "%s", ret_type);
{
int 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(struct %s *this", ret_type, mname, cls);
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
emit(")");
next_tok();
} else {
emit(", ");
int depth = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")") && depth == 0) break;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) depth++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) depth--;
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
emit(" struct %s", g_tok.text);
next_tok();
/* handle * or & suffix */
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, "*") || !strcmp(g_tok.text, "&"))) {
emit(" *");
next_tok();
}
continue;
}
/* builtin ref param: type &name -> type *name */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "&")) {
emit("*");
next_tok();
continue;
}
emit_tok();
next_tok();
}
emit(")");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) next_tok();
}
}
/* skip constructor initializer list: ": Base(args, ...)" */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) {
int bd = 0;
/* skip to '{' at depth 0 */
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) bd++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) bd--;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{") && bd == 0)
break;
next_tok();
}
}
emit(" {\n");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) next_tok();
/* ctor: chain the base-class ctor (default) */
if (is_ctor && ci && ci->base[0]) {
ClassInfo *bci = find_class(ci->base);
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);
int prev_dot = 0;
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 0)) {
if (g_tok.type == T_EOF) break;
/* namespace-qualified name: ns::name -> name */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "::")) {
next_tok(); /* consume '::' */
continue;
}
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; }
break;
}
if (g_tok.type == T_THIS) {
emit("this");
prev_dot = 0;
next_tok();
continue;
}
/* bare member name -> this->member (but not after . or ->) */
if (g_tok.type == T_IDENT && !prev_dot && mci && is_member(mci, g_tok.text)
&& !find_class(g_tok.text)) {
char path[64];
member_path(mci, g_tok.text, path, sizeof path);
if (!strcmp(path, "this"))
emit(" this->%s", g_tok.text);
else
emit(" %s.%s", path, g_tok.text);
prev_dot = 0;
next_tok();
continue;
}
/* local class variable: ClassType var(args) or ClassType var; */
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
char lcls[128];
Token n1, n2;
strncpy(lcls, g_tok.text, sizeof lcls - 1);
lcls[sizeof lcls - 1] = 0;
read_tok(&n1);
if (n1.type == T_IDENT) {
read_tok(&n2);
if (n2.type == T_PUNCT && !strcmp(n2.text, "(")) {
/* emit declaration + ctor call */
emit(" struct %s %s; %s_ctor(&%s",
lcls, n1.text, lcls, n1.text);
unread_tok(&n2); /* restore '(' */
next_tok(); /* g_tok = '(' */
next_tok(); /* -> first arg or ')' */
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) {
emit(")");
next_tok();
} else {
emit(", ");
int adepth = 0;
int ad_prev_dot = 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--;
/* this-> conv for args */
if (g_tok.type == T_IDENT && mci &&
is_member(mci, g_tok.text) &&
!find_class(g_tok.text) &&
!ad_prev_dot) {
char path[64];
member_path(mci, g_tok.text,
path, sizeof path);
if (!strcmp(path, "this"))
emit(" this->%s", g_tok.text);
else
emit(" %s.%s", path, g_tok.text);
} else {
emit_tok();
}
ad_prev_dot =
(g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ".") ||
!strcmp(g_tok.text, "->")));
next_tok();
}
emit(")");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
prev_dot = 0;
continue;
}
/* not a ctor call: restore both tokens */
unread_tok(&n2);
unread_tok(&n1);
} else {
unread_tok(&n1);
}
/* plain class name in body: emit with 'struct' prefix */
prev_dot = 0;
if (find_class(g_tok.text))
emit(" struct %s", g_tok.text);
else
emit_tok();
next_tok();
continue;
}
normal_emit:
/* track . and -> so o.re is not converted */
prev_dot = (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ".") || !strcmp(g_tok.text, "->")));
if (g_tok.type == T_IDENT && find_class(g_tok.text))
emit(" struct %s", g_tok.text);
else
emit_tok();
next_tok();
}
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
emit("}\n\n");
}
/* ---------------- top-level ---------------- */
/* free operator overload definition at top level:
Vec operator+(Vec a, Vec b) { ... }
becomes:
struct Vec Vec_add(struct Vec *this, struct Vec b) { ... }
The first parameter is treated as the left operand ("this"),
and references to it inside the body are rewritten. */
static void parse_free_operator(const char *ret_type, const char *name)
{
char cls[128] = "";
char tmp[256];
char retbuf[160];
int ret_is_class = 0;
int i, n = 0;
/* trim ret_type */
snprintf(retbuf, sizeof retbuf, "%s", ret_type);
{
int rl = (int)strlen(retbuf);
while (rl > 0 && (retbuf[rl-1] == ' ' || retbuf[rl-1] == '\t'))
retbuf[--rl] = 0;
}
/* find first identifier in return type that is a known class */
for (i = 0; retbuf[i]; i++) {
if (isalnum((unsigned char)retbuf[i]) || retbuf[i] == '_') {
if (n < 127) tmp[n++] = retbuf[i];
} else {
if (n > 0) {
tmp[n] = 0;
if (find_class(tmp)) {
strncpy(cls, tmp, sizeof cls - 1);
cls[sizeof cls - 1] = 0;
ret_is_class = 1;
break;
}
n = 0;
}
}
}
if (!cls[0] && n > 0) {
tmp[n] = 0;
if (find_class(tmp)) {
strncpy(cls, tmp, sizeof cls - 1);
cls[sizeof cls - 1] = 0;
ret_is_class = 1;
}
}
/* otherwise use the first parameter's class */
if (!cls[0] && g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
Token t1;
read_tok(&t1);
if (t1.type == T_IDENT && find_class(t1.text))
strncpy(cls, t1.text, sizeof cls - 1);
unread_tok(&t1);
}
if (!cls[0]) {
/* operator on non-class types: skip the whole definition */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
int d = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) d++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
d--;
if (d == 0) { next_tok(); break; }
}
next_tok();
}
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
int d = 1;
next_tok();
while (d > 0 && g_tok.type != T_EOF) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) d++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) d--;
next_tok();
}
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
emit("/* skipped non-class operator */\n");
return;
}
{
char suf[32];
char mname[256];
char fname[64] = "";
mangle_operator(name, suf, sizeof suf);
snprintf(mname, sizeof mname, "%s%s", cls, suf);
/* signature */
if (ret_is_class)
emit("struct %s %s(struct %s *this", cls, mname, cls);
else
emit("%s %s(struct %s *this", retbuf, mname, cls);
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
emit(")");
next_tok();
} else {
int pidx = 0, psep = 0, pdepth = 0;
for (;;) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")") &&
pdepth == 0) break;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) pdepth++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) pdepth--;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",") &&
pdepth == 0) {
pidx++;
psep = 1;
next_tok();
continue;
}
if (pidx == 0) {
/* first param becomes 'this': skip type, capture name */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT &&
(!strcmp(nxt.text, ",") || !strcmp(nxt.text, ")"))) {
strncpy(fname, g_tok.text, sizeof fname - 1);
fname[sizeof fname - 1] = 0;
unread_tok(&nxt);
next_tok();
continue;
}
unread_tok(&nxt);
}
next_tok();
} else {
if (psep) { emit(", "); psep = 0; }
if (g_tok.type == T_IDENT && find_class(g_tok.text))
emit("struct %s", g_tok.text);
else
emit_tok();
next_tok();
}
}
emit(")");
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) next_tok();
}
}
emit(" {");
/* body: fname. -> this-> ; fname alone -> (*this) */
next_tok();
{
int bdepth = 0;
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") &&
bdepth == 0)) {
if (g_tok.type == T_EOF) break;
/* namespace-qualified name: ns::name -> name */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "::")) {
next_tok(); /* consume '::' */
continue;
}
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(" {");
next_tok();
continue;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
if (bdepth > 0) { bdepth--; emit("}"); }
next_tok();
continue;
}
/* return Cls(args) -> (struct Cls){args} */
if (g_tok.type == T_IDENT && !strcmp(g_tok.text, "return")) {
Token t1;
read_tok(&t1);
if (t1.type == T_IDENT && find_class(t1.text)) {
Token t2;
read_tok(&t2);
if (t2.type == T_PUNCT && !strcmp(t2.text, "(")) {
char rcls[128];
strncpy(rcls, t1.text, sizeof rcls - 1);
rcls[sizeof rcls - 1] = 0;
emit(" return (struct %s) {", rcls);
next_tok();
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) {
emit("}");
next_tok();
} else {
for (;;) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) break;
if (fname[0] && g_tok.type == T_IDENT &&
!strcmp(g_tok.text, fname)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT &&
(!strcmp(nxt.text, ".") ||
!strcmp(nxt.text, "->"))) {
Token nm;
read_tok(&nm);
if (nm.type == T_IDENT) {
emit(" this->%s", nm.text);
next_tok();
continue;
}
unread_tok(&nm);
emit(" this");
next_tok();
continue;
}
unread_tok(&nxt);
emit(" (*this)");
next_tok();
continue;
}
emit(" %s", g_tok.text);
next_tok();
}
emit("}");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
emit(";");
continue;
}
unread_tok(&t2);
}
unread_tok(&t1);
}
/* rewrite references to the first parameter */
if (fname[0] && g_tok.type == T_IDENT &&
!strcmp(g_tok.text, fname)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT &&
(!strcmp(nxt.text, ".") || !strcmp(nxt.text, "->"))) {
Token nm;
read_tok(&nm);
if (nm.type == T_IDENT) {
emit(" this->%s", nm.text);
next_tok();
continue;
}
unread_tok(&nm);
emit(" this");
next_tok();
continue;
}
unread_tok(&nxt);
emit(" (*this)");
next_tok();
continue;
}
emit_tok();
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
}
emit("}\n\n");
}
}
/* ---------------- 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)
{
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;
char iref[64][64]; /* builtin-type reference params (deref in body) */
int n_iref = 0;
int sig_pidx = 0; /* param index while scanning signature */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) {
/* member function: class is 'name', method follows */
g_tok.type = T_IDENT;
strncpy(g_tok.text, name, sizeof g_tok.text - 1);
g_tok.text[sizeof g_tok.text - 1] = 0;
parse_member_definition(ret_type);
return;
}
/* free operator overload: Ret operator+(params) { body }
-> Ret Class_suf(struct Class *this, rest) { body } */
if (!strncmp(name, "operator", 8)) {
parse_free_operator(ret_type, 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_PUNCT && !strcmp(g_tok.text, ",")) {
sig_pidx++;
emit_tok();
next_tok();
continue;
}
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, "*") || !strcmp(nxt.text, "&"))) {
/* Cls* / 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();
if (!strcmp(nxt.text, "&"))
add_fnref(name, sig_pidx);
}
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);
} else if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
/* builtin ref param: int &a -> int *a */
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "&")) {
emit(" %s*", g_tok.text);
next_tok(); /* g_tok = param name */
if (g_tok.type == T_IDENT && n_iref < 64) {
strncpy(iref[n_iref], g_tok.text, 63);
iref[n_iref][63] = 0;
n_iref++;
}
add_fnref(name, sig_pidx);
emit(" %s", g_tok.text); /* emit param name */
next_tok();
continue;
}
unread_tok(&nxt);
}
emit_tok();
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
int depth = 0;
emit(" {");
next_tok();
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 0)) {
if (g_tok.type == T_EOF) break;
/* namespace-qualified name: ns::name -> name */
if (g_tok.type == T_IDENT && !find_class(g_tok.text)) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "::")) {
next_tok(); /* consume '::' */
continue;
}
unread_tok(&nxt);
}
/* template function call: max(a, b) -> max_i(a, b) */
if (tpl_call_check(varcls, varname, nvars))
continue;
/* builtin ref param: a -> (*a) */
if (g_tok.type == T_IDENT && n_iref > 0) {
int i;
for (i = 0; i < n_iref; i++)
if (!strcmp(g_tok.text, iref[i])) break;
if (i < n_iref) {
emit(" (*%s)", g_tok.text);
next_tok();
continue;
}
}
/* function call with reference args: swap(x, y) -> swap(&x, &y) */
if (g_tok.type == T_IDENT) {
FnRef *fr = find_fnref(g_tok.text);
if (fr) {
Token nxt;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "(")) {
unread_tok(&nxt); /* push '(' back — next_tok calls will consume properly */
int ai = 0, adepth = 0;
for (int ki = 0; ki < n_iref; ki++) fprintf(stderr, " iref[%d]=%s", ki, iref[ki]);
fprintf(stderr, "\n");
for (int ki = 0; ki < fr->narg; ki++) fprintf(stderr, " DBG fnref arg[%d]=%d", ki, fr->arg[ki]);
fprintf(stderr, "\n");
emit(" %s(", g_tok.text);
next_tok(); /* consume name */
next_tok(); /* consume '(' */
if (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")"))) {
for (;;) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")") && adepth == 0)
break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, "(")) adepth++;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) adepth--;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ",") && adepth == 0) {
ai++;
emit(",");
next_tok();
continue;
}
if (fr_has_arg(fr, ai)) emit("&");
emit_tok();
next_tok();
}
}
emit(")");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
continue;
}
unread_tok(&nxt);
}
}
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; }
break;
}
/* variable declaration: struct Cls var; -> record var:Cls */
if (g_tok.type == T_IDENT && find_class(g_tok.text) &&
(nvars < 64)) {
char clsname[128];
strncpy(clsname, g_tok.text, sizeof clsname - 1);
clsname[sizeof clsname - 1] = 0;
emit(" struct %s", clsname);
next_tok();
/* reference declaration: Cls& r = p -> struct Cls* r = &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();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "=")) {
emit(" = &");
next_tok();
if (g_tok.type == T_IDENT) {
emit(" %s", g_tok.text);
next_tok();
}
}
}
continue;
}
/* 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(&amp);
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(&amp);
}
}
continue;
}
/* next token(s): var [= ...] */
if (g_tok.type == T_IDENT && nvars < 64) {
char vname[64];
strncpy(varcls[nvars], clsname, 127);
strncpy(varname[nvars], g_tok.text, 63);
strncpy(vname, g_tok.text, sizeof vname - 1);
vname[sizeof vname - 1] = 0;
nvars++;
emit(" %s", g_tok.text);
next_tok();
{
ClassInfo *cci = find_class(clsname);
{
/* operator assignment: var = a op b (works
even when the class has no ctor) */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "=")) {
/* var = expr1 op expr2 -> operator overload */
Token t1, t2, t3;
read_tok(&t1);
read_tok(&t2);
read_tok(&t3);
if (t1.type == T_IDENT && t2.type == T_PUNCT &&
t3.type == T_IDENT) {
int v1 = -1, v3 = -1, k;
for (k = 0; k < nvars; k++) {
if (!strcmp(t1.text, varname[k])) v1 = k;
if (!strcmp(t3.text, varname[k])) v3 = k;
}
if (v1 >= 0 && v3 >= 0) {
ClassInfo *vci = find_class(varcls[v1]);
const char *op = t2.text;
const char *suf = "op";
char mname[256];
if (vci) {
if (!strcmp(op, "+")) suf = "_add";
else if (!strcmp(op, "-")) suf = "_sub";
else if (!strcmp(op, "*")) suf = "_mul";
else if (!strcmp(op, "/")) suf = "_div";
else if (!strcmp(op, "==")) suf = "_eq";
else if (!strcmp(op, "<")) suf = "_lt";
else if (!strcmp(op, ">")) suf = "_gt";
snprintf(mname, sizeof mname, "%s%s",
varcls[v1], suf);
emit("; %s = %s(&%s, %s)",
vname, mname, t1.text, t3.text);
next_tok(); /* consume t3 */
continue;
}
}
}
unread_tok(&t3);
unread_tok(&t2);
unread_tok(&t1);
}
}
if (cci && cci->has_ctor) {
/* check for parenthesized ctor args: var(args) */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
emit("; %s_ctor(&%s", clsname, vname);
next_tok();
/* first arg or ')' */
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();
}
goto add_decls;
}
/* plain declaration: Cls var; -> default ctor */
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ";")) {
emit("; %s_ctor(&%s)", clsname, vname);
continue;
}
}
}
}
/* additional declarators: Cls a, b(2), c; */
add_decls:
while (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) {
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++;
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;
}
/* comparison operator overload: var1 op var2 (==, <, >) */
if (g_tok.type == T_IDENT) {
Token t1, t2;
int vi1 = -1, k;
for (k = 0; k < nvars; k++)
if (!strcmp(g_tok.text, varname[k])) { vi1 = k; break; }
if (vi1 >= 0) {
read_tok(&t1);
if (t1.type == T_PUNCT &&
(!strcmp(t1.text, "==") || !strcmp(t1.text, "!=") ||
!strcmp(t1.text, "<") || !strcmp(t1.text, ">") ||
!strcmp(t1.text, "<=") || !strcmp(t1.text, ">="))) {
read_tok(&t2);
if (t2.type == T_IDENT) {
int vi2 = -1;
for (k = 0; k < nvars; k++)
if (!strcmp(t2.text, varname[k])) { vi2 = k; break; }
if (vi2 >= 0) {
const char *op = t1.text;
const char *suf = "op";
char mname[256];
if (!strcmp(op, "==")) suf = "_eq";
else if (!strcmp(op, "!=")) suf = "_ne";
else if (!strcmp(op, "<")) suf = "_lt";
else if (!strcmp(op, ">")) suf = "_gt";
else if (!strcmp(op, "<=")) suf = "_le";
else if (!strcmp(op, ">=")) suf = "_ge";
snprintf(mname, sizeof mname, "%s%s",
varcls[vi1], suf);
emit(" %s(&%s, %s)", mname, varname[vi1], t2.text);
next_tok(); /* consume t2 */
continue;
}
}
unread_tok(&t2);
}
unread_tok(&t1);
}
}
/* member call: var.method(args) -> Class_method(&var, args) */
if (g_tok.type == T_IDENT) {
int vi;
for (vi = 0; vi < nvars; vi++) {
if (!strcmp(g_tok.text, varname[vi])) {
Token t1, t2, t3;
/* peek: . method ( */
read_tok(&t1);
if (t1.type == T_PUNCT && !strcmp(t1.text, ".")) {
read_tok(&t2);
if (t2.type == T_IDENT) {
read_tok(&t3);
if (t3.type == T_PUNCT && !strcmp(t3.text, "(")) {
char meth[128];
char mname[256];
strncpy(meth, t2.text, sizeof meth - 1);
meth[sizeof meth - 1] = 0;
snprintf(mname, sizeof mname, "%s_%s",
varcls[vi], meth);
emit(" %s(&%s", mname, varname[vi]);
/* peek already consumed: var . method (
g_tok is still the var name. Read one
token to reach first arg or ')' */
next_tok(); /* -> first arg or ) */
/* now g_tok is first arg or ')' */
if (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")"))) {
emit(", ");
int adepth = 0;
int carg = 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--;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ",") && adepth == 0) {
carg++;
emit(",");
next_tok();
continue;
}
if (carg < 16) {
FnRef *fr = find_fnref(mname);
if (fr && fr_has_arg(fr, carg)) emit("&");
}
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;
}
}
}
/* 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;
}
}
}
/* 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;
read_tok(&t1);
if (t1.type == T_PUNCT && !strcmp(t1.text, "->")) {
/* method call via -> */
Token t2;
read_tok(&t2);
if (t2.type == T_IDENT) {
Token t3;
read_tok(&t3);
if (t3.type == T_PUNCT &&
!strcmp(t3.text, "(")) {
char meth[128], 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)) {
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();
next_tok();
next_tok();
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);
} else if (t1.type == T_PUNCT && !strcmp(t1.text, ".")) {
/* data member via ref: p.field -> p->field */
Token t2;
read_tok(&t2);
if (t2.type == T_IDENT) {
emit(" %s->%s", g_tok.text, t2.text);
next_tok(); /* consume '.' */
next_tok(); /* consume field name */
goto next_body_tok;
}
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);
next_tok();
continue;
}
emit_tok();
next_tok();
next_body_tok:
;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
emit("}\n\n");
}
}
/* collect tokens until we hit a '(', ';', '{', '}', ',' or EOF.
The last identifier seen is the declared name; everything
before it is the type. Returns the name in 'name'. */
static void gather_decl(char *type, int typesz, char *name, int namesz)
{
type[0] = 0;
name[0] = 0;
for (;;) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_CPP_OPERATOR) {
/* operator overload: name is "operator+" etc */
char opname[64] = "operator";
next_tok();
/* read the operator symbol(s) */
if (g_tok.type == T_PUNCT) {
/* could be multi-char like ==, <<, etc */
strncat(opname, g_tok.text, sizeof opname - strlen(opname) - 1);
next_tok();
} else if (g_tok.type == T_IDENT &&
(!strcmp(g_tok.text, "new") || !strcmp(g_tok.text, "delete"))) {
strncat(opname, "_", sizeof opname - strlen(opname) - 1);
strncat(opname, g_tok.text, sizeof opname - strlen(opname) - 1);
next_tok();
}
/* operator() needs special handling: () */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
/* check for () */
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ")")) {
strncat(opname, "()", sizeof opname - strlen(opname) - 1);
next_tok();
} else {
/* unread the ( */
unread_tok(&g_tok);
/* restore ( token */
{
Token paren;
paren.type = T_PUNCT;
strcpy(paren.text, "(");
unread_tok(&paren);
}
next_tok();
}
}
strncpy(name, opname, namesz - 1);
name[namesz - 1] = 0;
return;
}
if (g_tok.type == T_PUNCT) {
const char *t = g_tok.text;
if (!strcmp(t, "(") || !strcmp(t, ";") || !strcmp(t, "{") ||
!strcmp(t, "}") || !strcmp(t, ",")) break;
/* punctuators like * & [] are part of the type/name */
strncat(type, t, typesz - strlen(type) - 1);
strncat(type, " ", typesz - strlen(type) - 1);
next_tok();
continue;
}
/* word token */
{
char word[256];
strncpy(word, g_tok.text, sizeof word - 1);
word[sizeof word - 1] = 0;
next_tok();
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, "(") || !strcmp(g_tok.text, "::"))) {
/* this word is the name */
strncpy(name, word, namesz - 1);
name[namesz - 1] = 0;
return;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) {
/* this word is the name of the first declarator */
strncpy(name, word, namesz - 1);
name[namesz - 1] = 0;
return;
}
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}") ||
!strcmp(g_tok.text, "{"))) {
/* word is the name (declarator before ; } {) */
strncpy(name, word, namesz - 1);
name[namesz - 1] = 0;
return;
}
/* it's part of the type */
strncat(type, word, typesz - strlen(type) - 1);
strncat(type, " ", typesz - strlen(type) - 1);
}
}
/* 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 (;;) {
char tybuf[512];
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_CLASS) { parse_class(); continue; }
if (g_tok.type == T_PREPROC) {
emit("%s\n", g_tok.text);
next_tok();
continue;
}
if (g_tok.type == T_STRUCT) {
/* named struct with a body: treat like a class (C++ struct
semantics: methods, ctors, operators work the same) */
{
Token nx1, nx2;
read_tok(&nx1);
if (nx1.type == T_IDENT) {
read_tok(&nx2);
if (nx2.type == T_PUNCT &&
(!strcmp(nx2.text, "{") || !strcmp(nx2.text, ":"))) {
unread_tok(&nx2);
unread_tok(&nx1);
parse_class();
continue;
}
unread_tok(&nx2);
}
unread_tok(&nx1);
}
/* plain C struct (anonymous or fwd decl): copy verbatim,
brace-depth aware so nested {} bodies don't confuse us */
next_tok();
emit("struct ");
{
int depth = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") &&
depth == 0) break;
emit_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) depth++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) depth--;
next_tok();
}
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
emit("}");
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) {
emit(";");
next_tok();
}
emit("\n");
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_NAMESPACE) {
/* namespace NAME { ... } -> flattened: parse the body at
top level; '}' (tracked by g_ns_open) closes it */
next_tok();
if (g_tok.type == T_IDENT) next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
next_tok();
g_ns_open++;
}
continue;
}
/* '}' closes a namespace block */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
if (g_ns_open > 0) g_ns_open--;
next_tok();
continue;
}
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 &&
(!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}"))) break;
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) {
emit("/* skipped */\n");
next_tok();
}
continue;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) {
emit(";\n");
next_tok();
continue;
}
{
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);
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT &&
(!strcmp(g_tok.text, ";") || !strcmp(g_tok.text, "}"))) break;
emit_tok();
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) { emit(";"); next_tok(); }
emit("\n");
}
}
}
}
int main(int argc, char **argv)
{
const char *infile = NULL, *outfile = NULL;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] == 'o' && i + 1 < argc)
outfile = argv[++i];
else if (argv[i][0] != '-')
infile = argv[i];
}
if (!infile) {
fprintf(stderr, "usage: pcc-cpp input.cpp [-o output.c]\n");
return 1;
}
g_in = fopen(infile, "r");
if (!g_in) { fprintf(stderr, "cannot open %s\n", infile); return 1; }
g_out = outfile ? fopen(outfile, "w") : stdout;
if (!g_out) { fprintf(stderr, "cannot open %s\n", outfile); return 1; }
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;
}