docs: 新增 libhttp/libtry/pymsg/README.robot.md 技术手册,更新 BotOps/README.md
This commit is contained in:
1 parent
8a14247aed
commit
e4831522a4
3 files changed
+2226
No files matched your search
@@ -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 源码分析生成,供开发团队参考。使用前请评估已知限制。*
|
||||
Reference in new issue
Block a user