- 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
2882 行
115 KiB
C
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(&);
|
|
if (amp.type == T_PUNCT && !strcmp(amp.text, "&")) {
|
|
read_tok(&vv);
|
|
if (vv.type == T_IDENT) {
|
|
emit(" = (struct %s *)&%s",
|
|
clsname, vv.text);
|
|
next_tok();
|
|
continue;
|
|
}
|
|
unread_tok(&vv);
|
|
}
|
|
unread_tok(&);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
/* next token(s): var [= ...] */
|
|
if (g_tok.type == T_IDENT && nvars < 64) {
|
|
char vname[64];
|
|
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;
|
|
}
|
|
|