feat: C++ 阶段 2 - 运算符重载支持
- 成员运算符: 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
这个提交包含在:
二进制
二进制文件未显示。
+56
-78
@@ -1,86 +1,64 @@
|
|||||||
# PCC C++ 阶段 1 实现计划(修订版)
|
# PCC C++ 阶段 2 实现计划
|
||||||
|
|
||||||
## 架构决策:C++ 前端转换器(pcc-cpp)
|
## 目标
|
||||||
|
在阶段 1(class/成员函数/构造/继承)基础上,增加:
|
||||||
|
1. **运算符重载** — `operator+`、`operator==` 等
|
||||||
|
2. **命名空间** — `namespace` 作用域
|
||||||
|
3. **引用** — `Type&` 引用参数/返回
|
||||||
|
4. **模板基础** — `template <typename T>` 简单模板
|
||||||
|
5. **虚函数/多态** — `virtual` 方法(vtable 简化版)
|
||||||
|
|
||||||
**不在核心解析器中实现 C++**(风险高、工程大),而是创建一个
|
## 设计
|
||||||
**独立的 C++ → C 转换器**,把 C++ 代码翻译成等效 C 代码,
|
|
||||||
再交给现有 pcc 编译。这是 cfront/Comeau 的成熟做法。
|
|
||||||
|
|
||||||
## 转换规则
|
### 1. 运算符重载
|
||||||
|
|
||||||
### 1. class 声明
|
|
||||||
```cpp
|
```cpp
|
||||||
class Point {
|
class Complex {
|
||||||
public:
|
public:
|
||||||
int x, y;
|
double re, im;
|
||||||
int sum();
|
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 <typename T>
|
||||||
|
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; }
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
→
|
- 简化:虚函数当普通成员函数处理(无 vtable)
|
||||||
```c
|
- 通过基类指针调用子类方法需运行时绑定(暂不支持)
|
||||||
struct Point { int x, y; };
|
|
||||||
int Point_sum(struct Point *this);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 成员函数定义
|
## 实现顺序
|
||||||
```cpp
|
1. 运算符重载
|
||||||
int Point::sum() { return x + y; }
|
2. 命名空间
|
||||||
```
|
3. 引用
|
||||||
→
|
4. 模板(跳过)
|
||||||
```c
|
5. 虚函数(降级为普通成员函数)
|
||||||
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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
+789
-15
@@ -21,6 +21,7 @@ typedef enum {
|
|||||||
T_CLASS, T_STRUCT, T_PUBLIC, T_PRIVATE, T_PROTECTED,
|
T_CLASS, T_STRUCT, T_PUBLIC, T_PRIVATE, T_PROTECTED,
|
||||||
T_THIS, T_NEW, T_DELETE, T_NAMESPACE, T_USING, T_VIRTUAL,
|
T_THIS, T_NEW, T_DELETE, T_NAMESPACE, T_USING, T_VIRTUAL,
|
||||||
T_INLINE, T_FRIEND, T_TEMPLATE, T_CONSTEXPR,
|
T_INLINE, T_FRIEND, T_TEMPLATE, T_CONSTEXPR,
|
||||||
|
T_CPP_OPERATOR,
|
||||||
T_PREPROC, T_PUNCT
|
T_PREPROC, T_PUNCT
|
||||||
} TokType;
|
} TokType;
|
||||||
|
|
||||||
@@ -79,6 +80,36 @@ static int is_member(ClassInfo *ci, const char *m)
|
|||||||
return 0;
|
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 */
|
/* find access path for a member: base-class member -> this->__base */
|
||||||
static void member_path(ClassInfo *ci, const char *m, char *out, int outsz)
|
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, "friend")) return T_FRIEND;
|
||||||
if (!strcmp(s, "template")) return T_TEMPLATE;
|
if (!strcmp(s, "template")) return T_TEMPLATE;
|
||||||
if (!strcmp(s, "constexpr")) return T_CONSTEXPR;
|
if (!strcmp(s, "constexpr")) return T_CONSTEXPR;
|
||||||
|
if (!strcmp(s, "operator")) return T_CPP_OPERATOR;
|
||||||
return T_IDENT;
|
return T_IDENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +222,14 @@ static void next_tok_internal(Token *t)
|
|||||||
c = getc2();
|
c = getc2();
|
||||||
if (c == EOF) { t->type = T_EOF; t->text[0] = 0; return; }
|
if (c == EOF) { t->type = T_EOF; t->text[0] = 0; return; }
|
||||||
if (isspace(c)) continue;
|
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 == '/') {
|
if (c == '/') {
|
||||||
int c2 = getc2();
|
int c2 = getc2();
|
||||||
if (c2 == '/') {
|
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);
|
snprintf(mname, sizeof mname, "%s_ctor", cls);
|
||||||
else if (is_dtor)
|
else if (is_dtor)
|
||||||
snprintf(mname, sizeof mname, "%s_dtor", cls);
|
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);
|
snprintf(mname, sizeof mname, "%s_%s", cls, name);
|
||||||
|
|
||||||
/* constructors/destructors return void */
|
/* constructors/destructors return void */
|
||||||
if (is_ctor || is_dtor)
|
if (is_ctor || is_dtor)
|
||||||
ret_type = "void";
|
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);
|
proto_emit(" %s %s(struct %s *this", ret_type, mname, cls);
|
||||||
g_params[0] = 0;
|
g_params[0] = 0;
|
||||||
@@ -422,6 +482,14 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
|
|||||||
break;
|
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_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 */
|
/* smart spacing for prototype params */
|
||||||
{
|
{
|
||||||
const char *tt = g_tok.text;
|
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] ? ", " : "");
|
g_params[0] ? ", " : "");
|
||||||
proto_emit("%s) {\n", g_params);
|
proto_emit("%s) {\n", g_params);
|
||||||
next_tok(); /* consume '{' */
|
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 bd = 1;
|
||||||
|
int prev_dot = 0;
|
||||||
while (bd > 0) {
|
while (bd > 0) {
|
||||||
if (g_tok.type == T_EOF) break;
|
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_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; }
|
if (g_tok.type == T_THIS) { proto_emit("this"); prev_dot = 0; next_tok(); continue; }
|
||||||
/* bare member name -> this->member (or this->__base->member) */
|
/* bare member name -> this->member (not after . or ->) */
|
||||||
if (g_tok.type == T_IDENT && mci && is_member(mci, g_tok.text)) {
|
if (g_tok.type == T_IDENT && !prev_dot && mci && is_member(mci, g_tok.text)
|
||||||
|
&& !find_class(g_tok.text)) {
|
||||||
char path[64];
|
char path[64];
|
||||||
member_path(mci, g_tok.text, path, sizeof path);
|
member_path(mci, g_tok.text, path, sizeof path);
|
||||||
/* path is "this" -> this->x ; "this->__base" -> this->__base.x */
|
|
||||||
if (!strcmp(path, "this"))
|
if (!strcmp(path, "this"))
|
||||||
proto_emit(" this->%s", g_tok.text);
|
proto_emit(" this->%s", g_tok.text);
|
||||||
else
|
else
|
||||||
proto_emit(" %s.%s", path, g_tok.text);
|
proto_emit(" %s.%s", path, g_tok.text);
|
||||||
|
prev_dot = 0;
|
||||||
next_tok();
|
next_tok();
|
||||||
continue;
|
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);
|
proto_emit(" %s", g_tok.text);
|
||||||
next_tok();
|
next_tok();
|
||||||
}
|
}
|
||||||
@@ -614,18 +827,39 @@ static void parse_member_definition(const char *ret_type)
|
|||||||
cls[sizeof cls - 1] = 0;
|
cls[sizeof cls - 1] = 0;
|
||||||
next_tok();
|
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();
|
||||||
if (g_tok.type != T_IDENT) { emit("/* bad member def */\n"); return; }
|
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);
|
strncpy(method, g_tok.text, sizeof method - 1);
|
||||||
method[sizeof method - 1] = 0;
|
method[sizeof method - 1] = 0;
|
||||||
|
next_tok();
|
||||||
|
}
|
||||||
if (!strcmp(method, cls)) is_ctor = 1;
|
if (!strcmp(method, cls)) is_ctor = 1;
|
||||||
if (method[0] == '~') is_dtor = 1;
|
if (method[0] == '~') is_dtor = 1;
|
||||||
next_tok();
|
|
||||||
|
|
||||||
if (is_ctor)
|
if (is_ctor)
|
||||||
snprintf(mname, sizeof mname, "%s_ctor", cls);
|
snprintf(mname, sizeof mname, "%s_ctor", cls);
|
||||||
else if (is_dtor)
|
else if (is_dtor)
|
||||||
snprintf(mname, sizeof mname, "%s_dtor", cls);
|
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);
|
snprintf(mname, sizeof mname, "%s_%s", cls, method);
|
||||||
ci = find_class(cls);
|
ci = find_class(cls);
|
||||||
if (ci) { if (is_ctor) ci->has_ctor = 1; if (is_dtor) ci->has_dtor = 1; }
|
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 */
|
/* constructors/destructors return void */
|
||||||
if (is_ctor || is_dtor)
|
if (is_ctor || is_dtor)
|
||||||
ret_type = "void";
|
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);
|
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 == 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_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();
|
emit_tok();
|
||||||
next_tok();
|
next_tok();
|
||||||
}
|
}
|
||||||
@@ -671,32 +925,124 @@ static void parse_member_definition(const char *ret_type)
|
|||||||
|
|
||||||
emit(" {\n");
|
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();
|
||||||
|
/* 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;
|
int depth = 0;
|
||||||
ClassInfo *mci = find_class(cls);
|
ClassInfo *mci = find_class(cls);
|
||||||
|
int prev_dot = 0;
|
||||||
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 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_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 (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
|
||||||
if (depth > 0) { depth--; emit("}"); next_tok(); continue; }
|
if (depth > 0) { depth--; emit("}"); next_tok(); continue; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (g_tok.type == T_THIS) {
|
if (g_tok.type == T_THIS) {
|
||||||
emit("this");
|
emit("this");
|
||||||
|
prev_dot = 0;
|
||||||
next_tok();
|
next_tok();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* bare member name -> this->member (or this->__base->member) */
|
/* bare member name -> this->member (but not after . or ->) */
|
||||||
if (g_tok.type == T_IDENT && mci && is_member(mci, g_tok.text)) {
|
if (g_tok.type == T_IDENT && !prev_dot && mci && is_member(mci, g_tok.text)
|
||||||
|
&& !find_class(g_tok.text)) {
|
||||||
char path[64];
|
char path[64];
|
||||||
member_path(mci, g_tok.text, path, sizeof path);
|
member_path(mci, g_tok.text, path, sizeof path);
|
||||||
if (!strcmp(path, "this"))
|
if (!strcmp(path, "this"))
|
||||||
emit(" this->%s", g_tok.text);
|
emit(" this->%s", g_tok.text);
|
||||||
else
|
else
|
||||||
emit(" %s.%s", path, g_tok.text);
|
emit(" %s.%s", path, g_tok.text);
|
||||||
|
prev_dot = 0;
|
||||||
next_tok();
|
next_tok();
|
||||||
continue;
|
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();
|
emit_tok();
|
||||||
next_tok();
|
next_tok();
|
||||||
}
|
}
|
||||||
@@ -707,6 +1053,264 @@ static void parse_member_definition(const char *ret_type)
|
|||||||
|
|
||||||
/* ---------------- top-level ---------------- */
|
/* ---------------- 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)
|
static void parse_function(const char *ret_type, const char *name)
|
||||||
{
|
{
|
||||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) {
|
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;
|
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);
|
emit("%s %s", ret_type, name);
|
||||||
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{"))) {
|
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{"))) {
|
||||||
if (g_tok.type == T_EOF) break;
|
if (g_tok.type == T_EOF) break;
|
||||||
@@ -756,9 +1367,51 @@ static void parse_function(const char *ret_type, const char *name)
|
|||||||
nvars++;
|
nvars++;
|
||||||
emit(" %s", g_tok.text);
|
emit(" %s", g_tok.text);
|
||||||
next_tok();
|
next_tok();
|
||||||
/* if this class has a ctor, call it after declaration */
|
|
||||||
{
|
{
|
||||||
ClassInfo *cci = find_class(clsname);
|
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) {
|
if (cci && cci->has_ctor) {
|
||||||
/* check for parenthesized ctor args: var(args) */
|
/* check for parenthesized ctor args: var(args) */
|
||||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
|
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();
|
!strcmp(g_tok.text, ")")) next_tok();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
/* plain declaration: Cls var; -> default ctor */
|
||||||
|
if (g_tok.type == T_PUNCT &&
|
||||||
|
!strcmp(g_tok.text, ";")) {
|
||||||
emit("; %s_ctor(&%s)", clsname, vname);
|
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;
|
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) */
|
/* member call: var.method(args) -> Class_method(&var, args) */
|
||||||
if (g_tok.type == T_IDENT) {
|
if (g_tok.type == T_IDENT) {
|
||||||
int vi;
|
int vi;
|
||||||
@@ -821,6 +1529,7 @@ static void parse_function(const char *ret_type, const char *name)
|
|||||||
/* now g_tok is first arg or ')' */
|
/* now g_tok is first arg or ')' */
|
||||||
if (!(g_tok.type == T_PUNCT &&
|
if (!(g_tok.type == T_PUNCT &&
|
||||||
!strcmp(g_tok.text, ")"))) {
|
!strcmp(g_tok.text, ")"))) {
|
||||||
|
emit(", ");
|
||||||
int adepth = 0;
|
int adepth = 0;
|
||||||
while (!(g_tok.type == T_PUNCT &&
|
while (!(g_tok.type == T_PUNCT &&
|
||||||
!strcmp(g_tok.text, ")") && adepth == 0)) {
|
!strcmp(g_tok.text, ")") && adepth == 0)) {
|
||||||
@@ -873,6 +1582,45 @@ static void gather_decl(char *type, int typesz, char *name, int namesz)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (g_tok.type == T_EOF) break;
|
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) {
|
if (g_tok.type == T_PUNCT) {
|
||||||
const char *t = g_tok.text;
|
const char *t = g_tok.text;
|
||||||
if (!strcmp(t, "(") || !strcmp(t, ";") || !strcmp(t, "{") ||
|
if (!strcmp(t, "(") || !strcmp(t, ";") || !strcmp(t, "{") ||
|
||||||
@@ -932,12 +1680,38 @@ static void parse_program(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g_tok.type == T_STRUCT) {
|
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();
|
next_tok();
|
||||||
emit("struct ");
|
emit("struct ");
|
||||||
|
{
|
||||||
|
int depth = 0;
|
||||||
while (!(g_tok.type == T_EOF)) {
|
while (!(g_tok.type == T_EOF)) {
|
||||||
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") &&
|
||||||
|
depth == 0) break;
|
||||||
emit_tok();
|
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();
|
next_tok();
|
||||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) break;
|
}
|
||||||
}
|
}
|
||||||
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
|
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
|
||||||
emit("}");
|
emit("}");
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户