- namespace NAME { } 块扁平化: 跳过关键字, 内容按顶层解析
- 表达式/函数体中 ns::name -> name (非类名 + :: 跳过)
- 外部成员定义 ns::Class::method -> Class_method (跳命名空间限定)
- 支持嵌套命名空间 outer::inner::Class::method
- using namespace ns; / using ns::name; 跳过
- 修复: gather_decl 已消费首个 :: 的流位置不对称问题
1890 行
74 KiB
C
1890 行
74 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) */
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
/* ---------------- 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;
|
|
va_start(ap, fmt);
|
|
vfprintf(g_out, fmt, ap);
|
|
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 void parse_member_decl(const char *cls, const char *ret_type, const char *mname_in)
|
|
{
|
|
char name[256];
|
|
char mname[256];
|
|
int is_ctor, is_dtor;
|
|
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);
|
|
}
|
|
/* 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)) {
|
|
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();
|
|
continue;
|
|
}
|
|
/* smart spacing for prototype params */
|
|
{
|
|
const char *tt = g_tok.text;
|
|
int c = tt[0];
|
|
if (g_tok.type == T_PUNCT) {
|
|
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);
|
|
}
|
|
{
|
|
int bd = 1;
|
|
int prev_dot = 0;
|
|
while (bd > 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);
|
|
}
|
|
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;
|
|
}
|
|
/* fall-through: emit token as-is */
|
|
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();
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
emit("struct %s {\n", clsname);
|
|
if (base[0])
|
|
emit(" struct %s __base;\n", base);
|
|
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) next_tok();
|
|
else { emit("};\n\n"); return; }
|
|
|
|
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}"))) {
|
|
if (g_tok.type == T_EOF) break;
|
|
if (g_tok.type == T_PUBLIC || g_tok.type == T_PRIVATE ||
|
|
g_tok.type == T_PROTECTED) {
|
|
next_tok();
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ":")) next_tok();
|
|
continue;
|
|
}
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) { next_tok(); continue; }
|
|
{
|
|
char tybuf[512] = "";
|
|
char tmp[256];
|
|
/* use gather_decl: type + name (stops at '(' or ';' or '}') */
|
|
gather_decl(tybuf, sizeof tybuf, tmp, sizeof tmp);
|
|
if (tmp[0] && g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
|
|
/* member function decl */
|
|
parse_member_decl(clsname, tybuf[0] ? tybuf : "int", tmp);
|
|
} else if (tmp[0]) {
|
|
/* data member(s): type name [more] ; */
|
|
ClassInfo *cci = find_class(clsname);
|
|
emit(" %s %s", tybuf, tmp);
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
|
|
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();
|
|
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);
|
|
}
|
|
{
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
|
|
static void parse_function(const char *ret_type, const char *name)
|
|
{
|
|
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;
|
|
}
|
|
|
|
emit("%s %s", ret_type, name);
|
|
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{"))) {
|
|
if (g_tok.type == T_EOF) break;
|
|
emit_tok();
|
|
next_tok();
|
|
}
|
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
|
|
int depth = 0;
|
|
char varcls[64][128]; /* variable name -> class name */
|
|
char varname[64][64];
|
|
int nvars = 0;
|
|
emit(" {");
|
|
next_tok();
|
|
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 0)) {
|
|
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);
|
|
}
|
|
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();
|
|
/* 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();
|
|
}
|
|
continue;
|
|
}
|
|
/* 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, c; */
|
|
while (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) {
|
|
emit(", ");
|
|
next_tok();
|
|
if (g_tok.type == T_IDENT && nvars < 64) {
|
|
strncpy(varcls[nvars], clsname, 127);
|
|
strncpy(varname[nvars], g_tok.text, 63);
|
|
nvars++;
|
|
emit(" %s", g_tok.text);
|
|
next_tok();
|
|
}
|
|
}
|
|
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;
|
|
while (!(g_tok.type == T_PUNCT &&
|
|
!strcmp(g_tok.text, ")") && adepth == 0)) {
|
|
if (g_tok.type == T_EOF) break;
|
|
if (g_tok.type == T_PUNCT &&
|
|
!strcmp(g_tok.text, "(")) adepth++;
|
|
if (g_tok.type == T_PUNCT &&
|
|
!strcmp(g_tok.text, ")")) adepth--;
|
|
emit_tok();
|
|
next_tok();
|
|
}
|
|
}
|
|
emit(")");
|
|
if (g_tok.type == T_PUNCT &&
|
|
!strcmp(g_tok.text, ")")) next_tok();
|
|
goto next_body_tok;
|
|
}
|
|
unread_tok(&t3);
|
|
}
|
|
unread_tok(&t2);
|
|
}
|
|
unread_tok(&t1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* class name used as type -> add 'struct' */
|
|
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
|
|
emit(" struct %s", g_tok.text);
|
|
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 */
|
|
}
|
|
|
|
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_USING || g_tok.type == T_TEMPLATE ||
|
|
g_tok.type == T_INLINE || g_tok.type == T_CONSTEXPR ||
|
|
g_tok.type == T_FRIEND || g_tok.type == T_VIRTUAL) {
|
|
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]) {
|
|
parse_function(tybuf[0] ? tybuf : "int", nmbuf);
|
|
} 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) */
|
|
emit("int ");
|
|
parse_function("int", "f");
|
|
} 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();
|
|
|
|
fclose(g_in);
|
|
if (g_out != stdout) fclose(g_out);
|
|
return 0;
|
|
}
|