docs: 新增 libhttp/libtry/pymsg/README.robot.md 技术手册,更新 BotOps/README.md

This commit is contained in:
qing committed 2026-07-30 12:38:31 +08:00
1 parent 8a14247aed
commit e4831522a4
3 files changed
+2226

No files matched your search

+856
View File
@@ -0,0 +1,856 @@
# libtry 模块技术手册
> 文档版本:2026-07-30
> 代码版本:try.c(~741 行)
> 语言:C(嵌入式解释器)
> 定位:教学级脚本解释器,用于操作界面 shell
---
## 一、模块概述
### 1.1 定位
**libtry** 是一个基于 C 语言手写的**教学级解释器**,魔改自 GitHub 上的 tryC 项目。用于解析 `.try` 脚本文件,设计目标是作为**操作界面的 shell**。
### 1.2 特点
| 特性 | 说明 |
|------|------|
| 手写解释器 | 仅 741 行实现完整的词法/语法/执行引擎 |
| 零依赖 | 仅依赖标准 C 库 + `console.h` 输出接口 |
| 宽字符支持 | 使用 `wchar_t`,支持中文/多语言脚本 |
| 递归下降 | 递归下降语法分析器,边解析边执行 |
| 嵌入式设计 | 提供 `init_tryc`/`enqueue_tryc`/`eval_tryc`/`free_tryc` 四个核心 API |
### 1.3 与微服务体系的关系
| 属性 | 说明 |
|------|------|
| 模块关系 | **独立底层库**,不直接与任何微服务交互 |
| 可能用途 | 作为前端 UI 的脚本解释器,或嵌入式脚本引擎 |
| 启动方式 | 被其他模块(可能是 gateway 或 static)动态加载调用 |
---
## 二、技术架构
### 2.1 架构图
```
┌─────────────────────────────────────────────────────────────┐
│ 上层应用 │
│ (gateway / static / 其他模块) │
└─────────────────────────┬───────────────────────────────────┘
│ 调用 API
┌─────────────────────────▼───────────────────────────────────┐
│ libtry 解释器 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 词法分析器 (next) │ │
│ │ 逐字符扫描 → 识别 token → 更新符号表 │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ │ token 流 │
│ ┌──────────────────────────▼──────────────────────────────┐ │
│ │ 递归下降语法分析器 │ │
│ │ statement() / expression() / factor() / term() │ │
│ │ 边解析边执行(解释器模式) │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────┐ │
│ │ 符号表 │ │
│ │ symtab[128] — 符号栈(变量/函数/数组) │ │
│ │ levelNum — 嵌套层级,支持作用域 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ 执行结果输出 │
│ printtovga() / 控制台输出 │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 执行流程
```
init_tryc() 初始化
│
├── memset(symtab, 0, ...) 清零符号表
├── 预注册关键字 (array, func, else, if, return, while, print, puts, read)
└── malloc(128) 分配源代码缓冲区
│
enqueue_tryc(wc) 追加字符(交互式输入)
│
└── src[top_src] = wc; src[++top_src] = L'\0';
│
eval_tryc() 执行
│
├── next() 获取第一个 token
├── while (token != L'\0') { statement(); } 逐条执行
└── 重置缓冲区
│
free_tryc() 释放资源
│
└── free(p_src);
```
### 2.3 词法分析流程
```
next() 函数:
│
├── 跳过空白符 (L' ', L'\t')
├── 跳过注释 (# 开头到换行)
├── 识别标识符 (字母/数字/下划线/中文)
│ ├── 在符号表中搜索
│ │ ├── 找到:根据 type 返回对应 token (Sym, FuncSym, ArraySym, Str)
│ │ └── 未找到:创建新符号,类型为 Void
├── 识别数字 (整数 + 小数)
├── 识别字符字面量 ('a')
├── 识别字符串字面量 ("hello")
├── 识别运算符 (=, ==, !=, <, >, <=, >=, ||, &&)
└── 识别标点符号 (;, ,, +, -, *, /, (, ), {, }, [, ])
```
---
## 三、代码结构
### 3.1 文件结构
```
libtry/
├── try.c # 核心解释器实现(~741 行)
├── t.try # 示例脚本(斐波那契数列)
├── test/ # 测试目录
│ ├── t_assign_basic.try # 赋值测试
│ ├── t_function_basic.try # 函数测试
│ └── t_output_basic.try # 输出测试
├── .gitignore
└── README.robot.md # 本文档
```
### 3.2 try.c 函数索引
| 函数 | 行号 | 功能 |
|------|------|------|
| `init_tryc()` | L650 | 初始化解释器,预注册关键字 |
| `free_tryc()` | L672 | 释放源代码缓冲区 |
| `enqueue_tryc(wc)` | L677 | 追加一个字符到缓冲区 |
| `eval_tryc()` | L687 | 解析并执行当前缓冲区脚本 |
| `next()` | L85 | 词法分析,获取下一个 token |
| `match(tk)` | L289 | 匹配并消费期望 token |
| `statement()` | L466 | 解析并执行语句 |
| `expression()` | L362 | 解析加减法表达式 |
| `term()` | L311 | 解析乘除法表达式 |
| `factor()` | L326 | 解析因子(变量/常量/函数调用/数组) |
| `function()` | L616 | 执行函数调用 |
| `boolexp()` | L378 | 解析布尔表达式 |
| `boolAND()` | L423 | 解析 AND 表达式(短路) |
| `boolOR()` | L438 | 解析 OR 表达式(短路) |
| `clear(level)` | L269 | 清理指定层级的符号 |
| `clearThis()` | L280 | 清理单个符号的动态内存 |
| `skipStatments()` | L453 | 跳过 if/else 未执行分支 |
| `skipBoolExpr()` | L413 | 跳过短路布尔表达式 |
| `__exit()` | L702 | 错误处理(死循环挂起) |
| `main()` | L710 | 文件执行模式(仅 `_DEBUG` 编译) |
### 3.3 核心数据结构
#### 符号表(symStruct)
```c
typedef struct symStruct {
int type; // 类型:Num, Char, Str, Array, Func
wchar_t name[16]; // 符号名(MAXNAMESIZE)
double value; // 值或数组/字符串长度
union {
wchar_t *funcp; // 函数指针或字符串内容
struct symStruct* list; // 数组元素指针
} pointer;
int levelNum; // 嵌套层级(作用域)
} symbol;
```
#### Token 类型枚举
```c
enum {
Num = 128, // 数字
Char, // 字符字面量
Str, // 字符串
Array, // array 关键字
Func, // func 关键字
Else, // else 关键字
If, // if 关键字
Return, // return 关键字
While, // while 关键字
Print, // print 关键字
Puts, // puts 关键字
Read, // read 关键字
Assign, // = 赋值
OR, // ||
AND, // &&
Equal, // ==
Sym, // 变量符号
FuncSym, // 函数符号
ArraySym, // 数组符号
Void, // 未初始化
Nequal, // !=
LessEqual, // <=
GreatEqual // >=
};
```
#### 全局变量
| 变量 | 类型 | 说明 |
|------|------|------|
| `symtab[128]` | `symbol[]` | 符号表栈(SYMTABSIZE) |
| `symPointer` | `int` | 符号表栈指针 |
| `currentlevel` | `int` | 当前嵌套层级 |
| `src` | `wchar_t*` | 当前扫描位置 |
| `old_src` | `wchar_t*` | 行首位置(调试用) |
| `p_src` | `wchar_t*` | 源代码缓冲区起始 |
| `top_src` | `int` | 缓冲区写入位置 |
| `token` | `int` | 当前 token 类型 |
| `token_val` | `union tokenValue` | 当前 token 值 |
| `return_val` | `double` | 函数返回值 |
| `compileState` | `int` | 编译状态(debug/run) |
---
## 四、API 接口
### 4.1 初始化与销毁
#### init_tryc()
```c
int init_tryc(void);
```
**功能**:初始化解释器
- 清零符号表
- 预注册 8 个关键字(array, func, else, if, return, while, print, puts, read)
- 分配 128 字节宽字符缓冲区
**返回值**:
- `0`:成功
- `-1`:内存分配失败
#### free_tryc()
```c
void free_tryc(void);
```
**功能**:释放源代码缓冲区
### 4.2 输入与执行
#### enqueue_tryc(wc)
```c
int enqueue_tryc(wchar_t wc);
```
**功能**:追加一个字符到源代码缓冲区(用于交互式输入)
**参数**:
- `wc`:要追加的宽字符
**返回值**:
- `0`:成功
- `-1`:缓冲区已满
#### eval_tryc()
```c
void eval_tryc(void);
```
**功能**:解析并执行当前缓冲区中的脚本
**说明**:执行完成后会重置缓冲区。
### 4.3 典型使用示例
#### 交互式模式
```c
#include "try.c"
int main() {
init_tryc();
// 交互式输入
wchar_t input;
while (read_input(&input)) {
enqueue_tryc(input);
if (input == L'\n') {
eval_tryc(); // 遇到换行执行
}
}
free_tryc();
return 0;
}
```
#### 文件执行模式
```c
#include "try.c"
int main(int argc, char **argv) {
init_tryc();
// 读取文件
FILE *file = fopen(argv[1], "r");
wchar_t wc;
int i = 0;
while ((wc = fgetwc(file)) != WEOF) {
src[i++] = wc;
}
src[i] = L'\0';
fclose(file);
// 执行
eval_tryc();
clear(0); // 清理全局符号
free_tryc();
return 0;
}
```
---
## 五、语言特性
### 5.1 数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| `Num` | 数字(整数或浮点) | `a = 5;`, `b = 3.14;` |
| `Char` | 单个字符 | `c = 'A';` |
| `Str` | 字符串(宽字符) | `s = "hello";` |
| `Array` | 数组(动态分配) | `array arr(15);` |
| `Func` | 函数 | `func name{...};` |
### 5.2 变量与作用域
#### 变量声明
变量通过首次赋值自动声明:
```
x = 5; // 声明并初始化数字变量
name = "hello"; // 声明并初始化字符串变量
flag = 'A'; // 声明并初始化字符变量
```
#### 作用域
- 全局变量:在顶层声明
- 局部变量:在函数内声明,函数结束后自动销毁
- 符号表使用栈结构,`levelNum` 标识嵌套层级
### 5.3 运算符
#### 算术运算符
| 运算符 | 说明 | 示例 |
|--------|------|------|
| `+` | 加法 | `a + b` |
| `-` | 减法 | `a - b` |
| `*` | 乘法 | `a * b` |
| `/` | 除法 | `a / b` |
#### 比较运算符
| 运算符 | 说明 | 示例 |
|--------|------|------|
| `>` | 大于 | `a > b` |
| `<` | 小于 | `a < b` |
| `>=` | 大于等于 | `a >= b` |
| `<=` | 小于等于 | `a <= b` |
| `==` | 等于 | `a == b` |
| `!=` | 不等于 | `a != b` |
#### 逻辑运算符
| 运算符 | 说明 | 示例 |
|--------|------|------|
| `\|\|` | 逻辑或(短路) | `a \|\| b` |
| `&&` | 逻辑与(短路) | `a && b` |
| `!` | 逻辑非 | `!a` |
#### 赋值运算符
| 运算符 | 说明 | 示例 |
|--------|------|------|
| `=` | 赋值 | `a = 5;` |
### 5.4 控制流
#### if/else 语句
```
if (condition) {
// true 分支
}
else {
// false 分支
}
```
#### while 循环
```
while (condition) {
// 循环体
}
```
#### return 语句
```
func name{
return(value);
};
```
### 5.5 函数
#### 函数定义
```
func function_name{
// 函数体
return(return_value);
};
```
**特性**:
- 支持递归调用
- 支持参数传递(通过符号表)
- `return` 语句会终止函数执行并返回值
#### 函数调用
```
result = function_name(arg1, arg2);
```
### 5.6 数组
#### 数组声明
```
array arr(size);
```
- `size` 必须是正整数
- 数组元素初始化为 0
- 数组元素类型为 `Num`(double)
#### 数组访问与赋值
```
arr[0] = 10;
value = arr[i];
```
### 5.7 输入输出
#### print()
```
print(expression);
```
**功能**:输出数字
**示例**:
```
print(42); // 输出: 42.000000
print(3.14); // 输出: 3.140000
print(arr[0]); // 输出数组元素
```
#### puts()
```
puts("string");
```
**功能**:输出字符串
**示例**:
```
puts("Hello, World!");
```
#### read()
```
read(variable);
```
**功能**:读取输入(当前实现返回 -1)
### 5.8 注释
```
# This is a comment
```
注释从 `#` 开始到行尾结束。
---
## 六、示例脚本
### 6.1 斐波那契数列
```
# Fibonacci sequence
func fun{
if(x <= 2){
return(1);
}
y = 0;
x = x - 1;
y = fun(x);
x = x - 1;
return(y + fun(x));
};
# 计算 1-15 的斐波那契数列并存入数组
array arr(15);
x = 0;
while(x<15){
arr[x] = fun(x) + 1000;
x = x + 1;
}
puts("Fibonacci sequence:");
i = 0;
while(i < 15){
print(arr[i]);
i=i+1;
}
```
### 6.2 基础赋值
```
a = 5;
b = "ok";
print(5);
puts(b);
```
### 6.3 基础函数
```
func f_test{
return(0);
};
r = f_test();
print(r);
```
### 6.4 基础输出
```
print(1);
puts("1");
```
---
## 七、已知限制
### 🔴 严重问题
| ID | 位置 | 描述 | 影响 |
|----|------|------|------|
| L1 | [L20](file:///Users/qingsong/micro_servers/libtry/try.c#L20) | **缓冲区大小固定**:`POOLSIZE = 128` 字节,无法修改 | 长脚本将溢出 |
| L2 | [L702](file:///Users/qingsong/micro_servers/libtry/try.c#L702) | **`__exit()` 死循环**:`while(1);` 错误时直接挂死程序 | 无法恢复 |
| L3 | [L186](file:///Users/qingsong/micro_servers/libtry/try.c#L186) | **字符串内存泄漏**:`malloc` 分配的字符串在重新赋值时未释放 | 长期运行泄漏 |
| L4 | [L528](file:///Users/qingsong/micro_servers/libtry/try.c#L528) | **数组越界**:无越界检查 | 未定义行为 |
### 🟡 中等问题
| ID | 位置 | 描述 |
|----|------|------|
| L5 | [L598](file:///Users/qingsong/micro_servers/libtry/try.c#L598) | **`puts` 实现存疑**:原 `wprintf` 被注释,改用 `printtovga` |
| L6 | [L518](file:///Users/qingsong/micro_servers/libtry/try.c#L518) | **`clearThis()` 被注释**:作者注释说"不知为何需要注释了才能跑" |
| L7 | [L706](file:///Users/qingsong/micro_servers/libtry/try.c#L706) | **文件模式仅限 `_DEBUG`**:正常构建时 `main()` 不编译 |
| L8 | [L103](file:///Users/qingsong/micro_servers/libtry/try.c#L103) | **栈缓冲区溢出风险**:`nameBuffer[16]` 但标识符长度限制由 `MAXNAMESIZE=16` 控制 |
| L9 | [L21](file:///Users/qingsong/micro_servers/libtry/try.c#L21) | **符号表大小固定**:`SYMTABSIZE = 128` 个符号 |
### 🟢 轻微问题
| ID | 描述 |
|----|------|
| L10 | 错误处理过于简单,`match()` 失败直接 `__exit(-1)` |
| L11 | 大量被注释的调试代码(`//wprintf(...)`) |
| L12 | 全局变量过多(`symtab`, `token`, `token_val`, `src`, `old_src`, `currentlevel` 等) |
| L13 | 无并发支持,非线程安全 |
| L14 | 无单元测试 |
### 修复建议
#### 优先级 1(安全相关)
1. **修复数组越界**:在 [L528](file:///Users/qingsong/micro_servers/libtry/try.c#L528) 添加检查
```c
if (index < 0 || index >= s->value) {
printtovga(L"Array index out of bounds\n");
__exit(-1);
}
```
2. **修复 `__exit()`**:改为返回错误码
```c
static int error_code = 0;
static void __exit(int code) {
error_code = code;
// 不挂起,让调用者处理错误
}
```
3. **修复字符串内存泄漏**:在赋值前释放旧字符串
```c
else if (token == Str) {
if (s->pointer.funcp != NULL) {
free(s->pointer.funcp); // 释放旧字符串
}
s->pointer.funcp = (wchar_t*)token_val.ptr;
s->type = Str;
match(Str);
}
```
#### 优先级 2(功能增强)
4. **动态缓冲区**:将 `POOLSIZE` 改为可配置或动态扩容
5. **错误回调**:添加错误回调函数,让调用者处理错误
6. **符号表扩容**:允许 `SYMTABSIZE` 动态增长
---
## 八、开发指南
### 8.1 添加新关键字
在 [init_tryc()](file:///Users/qingsong/micro_servers/libtry/try.c#L650) 中添加:
```c
// 1. 在 enum 中添加新 token 类型
enum {
Num = 128, Char, Str, Array, Func,
Else, If, Return, While, Print, Puts, Read,
Assign, OR, AND, Equal, Sym, FuncSym, ArraySym, Void,
Nequal, LessEqual, GreatEqual,
NewKeyword // 新增
};
// 2. 在 init_tryc() 中注册
src = L"array func else if return while print puts read new_keyword";
for (i = Array; i <= NewKeyword; ++i) {
next();
symtab[symPointer - 1].type = i;
}
// 3. 在 statement() 中添加处理逻辑
else if (token == NewKeyword) {
match(NewKeyword);
// 处理逻辑
}
```
### 8.2 添加新运算符
在 [next()](file:///Users/qingsong/micro_servers/libtry/try.c#L85) 中添加识别逻辑:
```c
// 例如:+= 运算符
else if (token == L'+') {
if (*src == L'=') {
src++;
token = AddAssign; // 新增 token 类型
}
}
```
在 [statement()](file:///Users/qingsong/micro_servers/libtry/try.c#L466) 中添加执行逻辑。
### 8.3 自定义输出
修改 `printtovga()` 的实现,或添加可配置的输出回调:
```c
// 函数指针类型
typedef void (*output_func)(const wchar_t*);
// 全局输出函数
static output_func current_output = default_output;
// 设置输出函数
void set_output_func(output_func func) {
current_output = func;
}
```
### 8.4 构建说明
#### 编译器要求
- C 编译器(GCC/Clang)
- 支持 C99 标准
- 支持宽字符(`wchar.h`)
#### 编译选项
```bash
# 基本编译
gcc -c try.c -o try.o -std=c99
# 调试模式(启用 main 函数)
gcc -c try.c -o try.o -std=c99 -D_DEBUG
# 优化编译
gcc -c try.c -o try.o -std=c99 -O2
```
#### 链接使用
```bash
# 创建测试程序
cat > test_main.c << 'EOF'
#include <stdio.h>
#include "try.h"
int main() {
init_tryc();
// 注入脚本
wchar_t script[] = L"print(42);";
for (int i = 0; script[i] != L'\0'; i++) {
enqueue_tryc(script[i]);
}
eval_tryc();
free_tryc();
return 0;
}
EOF
# 编译链接
gcc test_main.c try.o -o test_try -std=c99
./test_try
```
---
## 九、测试
### 9.1 现有测试
| 测试文件 | 内容 |
|----------|------|
| `test/t_assign_basic.try` | 变量赋值与输出 |
| `test/t_function_basic.try` | 函数定义与调用 |
| `test/t_output_basic.try` | print/puts 输出 |
| `t.try` | 斐波那契数列(复杂示例) |
### 9.2 运行测试
```bash
# 编译(调试模式)
gcc try.c -o try_interp -std=c99 -D_DEBUG
# 执行测试脚本
./try_interp test/t_assign_basic.try
./try_interp test/t_function_basic.try
./try_interp test/t_output_basic.try
./try_interp t.try
```
### 9.3 添加新测试
在 `test/` 目录下创建 `.try` 文件:
```
# test/test_new_feature.try
# 描述新功能
a = 10;
b = 20;
c = a + b;
print(c);
```
---
## 十、版本历史
| 版本 | 日期 | 主要变更 |
|------|------|---------|
| 当前 | 2025-08-27 | 增加宽字符支持,修复 Str 匹配 BUG,使用 longjmp 替代 exit() |
| 初始 | — | 魔改自 tryC 项目,手写简易解释器 |
### 修改记录(代码注释)
| 日期 | 作者 | 变更 |
|------|------|------|
| 2025-08-27 | qing | 增加宽字符支持 |
| 2025-08-27 | qing | 修复 Str 漏匹配的 BUG |
| 2025-08-27 | qing | 注明内存泄漏风险 |
| 2025-08-27 | qing | 使用 `longjmp()` 替代 `exit()` |
| 2026-07-07 | qing | 增加 `clearThis()` 函数,用于释放变量 |
---
## 附录
### A. 完整关键字列表
```
array, func, else, if, return, while, print, puts, read
```
### B. Token 类型完整列表
```
Num(128), Char(129), Str(130), Array(131), Func(132),
Else(133), If(134), Return(135), While(136), Print(137), Puts(138), Read(139),
Assign(140), OR(141), AND(142), Equal(143), Sym(144), FuncSym(145), ArraySym(146), Void(147),
Nequal(148), LessEqual(149), GreatEqual(150)
```
### C. 限制常量
| 常量 | 值 | 说明 |
|------|-----|------|
| `POOLSIZE` | 128 | 源代码缓冲区大小(字节) |
| `SYMTABSIZE` | 128 | 符号表大小 |
| `MAXNAMESIZE` | 16 | 标识符最大长度 |
| `RETURNFLAG` | DBL_MAX | return 语句标记 |
### D. 参考资源
- [Crafting Interpreters](https://craftinginterpreters.com/) — 解释器编写经典教程
- [Writing an Interpreter in Go](https://interpreterbook.com/) — Go 实现的解释器教程
- [tryC (GitHub)](https://github.com/) — 原始项目(已魔改)
---
*本文档基于 try.c 源码分析生成,供开发团队参考。使用前请评估已知限制。*