diff --git a/bin/pcc-cpp.exe b/bin/pcc-cpp.exe index fb032e9..4a0ed9b 100644 Binary files a/bin/pcc-cpp.exe and b/bin/pcc-cpp.exe differ diff --git a/docs/cpp-plan.md b/docs/cpp-plan.md index 5d69c3b..de9c44d 100644 --- a/docs/cpp-plan.md +++ b/docs/cpp-plan.md @@ -1,86 +1,64 @@ -# PCC C++ 阶段 1 实现计划(修订版) +# PCC C++ 阶段 2 实现计划 -## 架构决策:C++ 前端转换器(pcc-cpp) +## 目标 +在阶段 1(class/成员函数/构造/继承)基础上,增加: +1. **运算符重载** — `operator+`、`operator==` 等 +2. **命名空间** — `namespace` 作用域 +3. **引用** — `Type&` 引用参数/返回 +4. **模板基础** — `template ` 简单模板 +5. **虚函数/多态** — `virtual` 方法(vtable 简化版) -**不在核心解析器中实现 C++**(风险高、工程大),而是创建一个 -**独立的 C++ → C 转换器**,把 C++ 代码翻译成等效 C 代码, -再交给现有 pcc 编译。这是 cfront/Comeau 的成熟做法。 +## 设计 -## 转换规则 - -### 1. class 声明 +### 1. 运算符重载 ```cpp -class Point { +class Complex { public: - int x, y; - int sum(); + double re, im; + Complex operator+(const Complex& o) { ... } +}; +Complex a, b; +Complex c = a + b; // → Complex_add(&c, &a, &b) +``` +- `operator+` → `Complex_operator_plus` → `Complex_add` +- `a + b` → `Complex_add(&tmp, &a, &b)` + +### 2. 命名空间 +```cpp +namespace math { int add(int a, int b); } +math::add(1,2); // → math_add(1,2) +``` +- 名称修饰:`ns::func` → `ns_func` +- `using namespace` 简化处理 + +### 3. 引用 +```cpp +void swap(int& a, int& b); // → void swap(int *a, int *b) +swap(x, y); // → swap(&x, &y) +``` +- 参数 `T& x` → `T *x`,调用时取地址 + +### 4. 模板基础 +```cpp +template +T max2(T a, T b) { return a > b ? a : b; } +max2(3, 5); // 实例化 max2_int +``` +- 简单模板:忽略模板头,按调用实例化 + +### 5. 虚函数(简化) +```cpp +class Shape { +public: + virtual int area() { return 0; } }; ``` -→ -```c -struct Point { int x, y; }; -int Point_sum(struct Point *this); -``` +- 简化:虚函数当普通成员函数处理(无 vtable) +- 通过基类指针调用子类方法需运行时绑定(暂不支持) -### 2. 成员函数定义 -```cpp -int Point::sum() { return x + y; } -``` -→ -```c -int Point_sum(struct Point *this) { return this->x + this->y; } -``` - -### 3. 成员调用 -```cpp -Point p; -p.sum(); // → Point_sum(&p) -``` -`p.x = 5` 直接用 struct 字段访问(无需转换) - -### 4. 构造函数 -```cpp -Point::Point(int a, int b) { x = a; y = b; } -``` -→ -```c -void Point_ctor(struct Point *this, int a, int b) { this->x = a; this->y = b; } -``` -局部对象声明时自动插入构造调用 - -### 5. 析构函数 -```cpp -Point::~Point() { } -``` -→ -```c -void Point_dtor(struct Point *this) { } -``` - -### 6. 简单继承 -```cpp -class Shape : public Point { }; -``` -→ -```c -struct Shape { struct Point __base; }; -``` - -## 实现形式 -- **pcc-cpp.c**:独立转换器,读取 .cpp,输出 .c -- pcc 检测 .cpp 文件时自动调用(内部集成) -- 用 pcc 自身编译(自举) - -## 测试用例 -```cpp -class Point { -public: - int x, y; - Point(int a, int b) { x = a; y = b; } - int sum() { return x + y; } -}; -int main() { - Point p(3, 4); - return p.sum() - 7; -} -``` +## 实现顺序 +1. 运算符重载 +2. 命名空间 +3. 引用 +4. 模板(跳过) +5. 虚函数(降级为普通成员函数) diff --git a/tools/pcc-cpp.c b/tools/pcc-cpp.c index c9fd1b0..6a4dda7 100644 --- a/tools/pcc-cpp.c +++ b/tools/pcc-cpp.c @@ -21,6 +21,7 @@ typedef enum { 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; @@ -79,6 +80,36 @@ static int is_member(ClassInfo *ci, const char *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) { @@ -179,6 +210,7 @@ static TokType kw_type(const char *s) 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; } @@ -190,6 +222,14 @@ static void next_tok_internal(Token *t) 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 == '/') { @@ -400,12 +440,32 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char snprintf(mname, sizeof mname, "%s_ctor", cls); else if (is_dtor) snprintf(mname, sizeof mname, "%s_dtor", cls); - else + 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; @@ -422,6 +482,14 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char 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; @@ -465,25 +533,170 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char 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; - if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { bd++; proto_emit(" {"); next_tok(); 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"); next_tok(); continue; } - /* bare member name -> this->member (or this->__base->member) */ - if (g_tok.type == T_IDENT && mci && is_member(mci, g_tok.text)) { + 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); - /* path is "this" -> this->x ; "this->__base" -> this->__base.x */ 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(); } @@ -614,18 +827,39 @@ static void parse_member_definition(const char *ret_type) cls[sizeof cls - 1] = 0; next_tok(); if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) next_tok(); - if (g_tok.type != T_IDENT) { emit("/* bad member def */\n"); return; } - strncpy(method, g_tok.text, sizeof method - 1); - method[sizeof method - 1] = 0; + 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; - next_tok(); if (is_ctor) snprintf(mname, sizeof mname, "%s_ctor", cls); else if (is_dtor) snprintf(mname, sizeof mname, "%s_dtor", cls); - else + 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; } @@ -633,6 +867,21 @@ static void parse_member_definition(const char *ret_type) /* 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); @@ -648,6 +897,11 @@ static void parse_member_definition(const char *ret_type) 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(); } @@ -671,33 +925,125 @@ static void parse_member_definition(const char *ret_type) 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; - 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, "{")) { 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 (or this->__base->member) */ - if (g_tok.type == T_IDENT && mci && is_member(mci, g_tok.text)) { + /* 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; } - emit_tok(); + /* 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(); } } @@ -707,6 +1053,264 @@ static void parse_member_definition(const char *ret_type) /* ---------------- 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; + 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, "::")) { @@ -718,6 +1322,13 @@ static void parse_function(const char *ret_type, const char *name) 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; @@ -756,9 +1367,51 @@ static void parse_function(const char *ret_type, const char *name) nvars++; emit(" %s", g_tok.text); next_tok(); - /* if this class has a ctor, call it after declaration */ { 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, "(")) { @@ -786,14 +1439,69 @@ static void parse_function(const char *ret_type, const char *name) !strcmp(g_tok.text, ")")) next_tok(); } continue; - } else { + } + /* 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; @@ -821,6 +1529,7 @@ static void parse_function(const char *ret_type, const char *name) /* 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)) { @@ -873,6 +1582,45 @@ static void gather_decl(char *type, int typesz, char *name, int namesz) 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, "{") || @@ -932,12 +1680,38 @@ static void parse_program(void) } 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 "); - while (!(g_tok.type == T_EOF)) { - emit_tok(); - next_tok(); - if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) break; + { + 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("}");