From d4b6fdc1f3dbfa1d120e8c17bd1276fe989b4ac6 Mon Sep 17 00:00:00 2001 From: Paze AI Date: Sun, 16 Aug 2026 18:01:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20C++=20=E9=98=B6=E6=AE=B5=202=20-=20?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6=E9=87=8D=E8=BD=BD=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 成员运算符: Class::operator+ -> Cls_add (类内定义 + 外部定义) - 自由运算符: Vec operator+(Vec a, Vec b) -> Cls_add(struct Cls *this, struct Cls b) 首参重写为 this, 函数体内引用重写 (a.x -> this->x) - 表达式转换: var = a op b -> var = Cls_op(&a, b); var1 op var2 -> Cls_op(&v1, v2) - 修复: struct 类路由到 parse_class (消除 T_STRUCT 逐字复制死循环) - 修复: 局部类变量 Complex r; 非破坏性 peek (read_tok 重写) - 修复: 多声明符 Cls a, b; 记录所有变量 - 修复: 无 ctor 类的运算符赋值不再被 has_ctor 门槛挡住 - 修复: 外部定义 Counter::inc() 丢失 next_tok 回归 - 新: 派生类 ctor 自动链式调用基类默认 ctor - 新: tokenizer 跳过 UTF-8 BOM --- bin/pcc-cpp.exe | Bin 104490 -> 74752 bytes docs/cpp-plan.md | 134 ++++---- tools/pcc-cpp.c | 816 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 851 insertions(+), 99 deletions(-) diff --git a/bin/pcc-cpp.exe b/bin/pcc-cpp.exe index fb032e9faee2ad0580d6a6b2b7980b5982956ca4..4a0ed9b17fc083fff4304a31ee9db0a87cf8ee55 100644 GIT binary patch delta 33156 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 literal 104490 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 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("}");