Commit 3802e0a9 by wzc-a

新建cargo: matlab自动解析为rustscript

parent bd680aed
...@@ -1732,6 +1732,15 @@ dependencies = [ ...@@ -1732,6 +1732,15 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rustscript-transpiler"
version = "0.1.0"
dependencies = [
"anyhow",
"regex",
"serde",
]
[[package]]
name = "rustversion" name = "rustversion"
version = "1.0.21" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
......
...@@ -13,6 +13,7 @@ members = [ ...@@ -13,6 +13,7 @@ members = [
"mems/examples/ds-powerflow/ds-3phase-pf", "mems/examples/ds-powerflow/ds-3phase-pf",
"mems/examples/ds-guizhou", "mems/examples/ds-guizhou",
"mems/examples/iesplan", "mems/examples/iesplan",
"rustscript-transpiler",
] ]
[workspace.package] [workspace.package]
......
[package]
name = "rustscript-transpiler"
version = "0.1.0"
edition = "2021"
description = "A transpiler that converts MATLAB code to RustScript"
[lib]
name = "rustscript_transpiler"
path = "src/lib.rs"
[dependencies]
regex = "1.10"
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
[dev-dependencies]
# 用于测试的依赖
# RustScript 语言规范
## 1. 语言概述
RustScript 是一种结合了 Rust 和 MATLAB 语法特征的脚本语言,专为科学计算和矩阵操作设计。它采用基于张量的数据结构,具有强类型推导和函数式编程特征。
## 2. 词法规则
### 2.1 标识符
```rustscript
// 有效标识符
variable_name
matrix1
bus_data
PQ_bus
```
### 2.2 数值字面量
```rustscript
// 整数
42
-17
0
// 浮点数
3.14
-2.718
1.23e-4
6.022e23
// 科学记数法
1.5e10
-2.3E-5
```
### 2.3 字符串字面量
```rustscript
// 字符串使用双引号
"Hello, World!"
"Power flow data for IEEE 14 bus"
"File path: /data/case14.txt"
```
### 2.4 注释
```rustscript
// 单行注释
/* 多行注释 */
/*
* 块注释
* 支持多行
*/
```
## 3. 数据类型
### 3.1 基本数据类型
- `f64`: 64位浮点数(默认数值类型)
- `i64`: 64位整数
- `bool`: 布尔值 (`true`, `false`)
- `string`: 字符串类型
- `complex`: 复数类型
**注意:RustScript没有结构体类型,所有数据都基于基本类型和张量。**
### 3.2 复数类型
```rustscript
// 复数创建
z1 = c(3.0, 4.0); // 3 + 4i
z2 = c(0, 1); // i
z3 = c(5.0, 0); // 实数
// 复数运算
result = z1 + z2 * 2.0;
magnitude = abs(z1);
phase = arg(z1);
```
### 3.3 张量类型
```rustscript
// 标量
scalar = 42.0;
// 1D 张量(一维张量)- shape为 [n]
vector = [1.0, 2.0, 3.0, 4.0]; // shape: [4]
// 2D 张量(二维张量)- shape为 [m, n]
matrix = [
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0]
]; // shape: [3, 3]
// 行向量(二维张量)- shape为 [1, n]
row_vector = [[1.0, 2.0, 3.0]]; // shape: [1, 3]
// 列向量(二维张量)- shape为 [m, 1]
col_vector = [[1.0], [2.0], [3.0]]; // shape: [3, 1]
// 3D 张量
tensor3d = [
[[1.0, 2.0], [3.0, 4.0]],
[[5.0, 6.0], [7.0, 8.0]]
]; // shape: [2, 2, 2]
```
**重要区别:**
- **一维张量**:shape为 [n],在运算时相当于行向量
- **二维张量**:包括矩阵、行向量 [1, n]、列向量 [m, 1]
- **行向量和列向量都属于二维张量**,但形状不同
### 3.4 数据组织方式
由于没有结构体,复杂数据通过张量和多个相关变量来组织:
```rustscript
// 电力系统数据组织(无结构体方式)
baseMVA = 100; // 基准功率
bus = [...]; // 母线数据矩阵
gen = [...]; // 发电机数据矩阵
branch = [...]; // 支路数据矩阵
// 不是: mpc.baseMVA, mpc.bus 等结构体访问方式
```
## 4. 变量和赋值
### 4.1 变量声明
```rustscript
// 变量赋值(类型推导)
baseMVA = 100;
voltage = 1.06;
name = "RustScript";
is_valid = true;
// 张量赋值
vector = [1, 2, 3, 4];
matrix = [[1, 2], [3, 4]];
```
### 4.2 常量声明
```rustscript
// 常量定义(约定使用大写)
PI = 3.14159265359;
EULER = 2.71828;
MAX_ITERATIONS = 1000;
// 电力系统常量示例
PQ = 1;
PV = 2;
REF = 3;
NONE = 4;
// 母线数据列索引
BUS_I = 0;
BUS_TYPE = 1;
PD = 2;
QD = 3;
```
## 5. 张量操作
### 5.1 张量索引
```rustscript
// 单个元素访问
element = get(matrix, row, col); // 获取指定位置的单个元素
// 注意:不支持 A[0,1] 这样的直接索引方式
// 错误写法:element = matrix[0, 1]; // 不支持
// 负索引(从末尾开始)
last_element = get(vector, -1);
```
### 5.2 张量切片
```rustscript
// slice函数用于取矩阵的行列
// slice(matrix, row_spec, col_spec)
// [0] 代表所有行或所有列
// 重要:返回类型取决于参数格式
// 1. 使用范围参数[start, end] - 返回二维张量
col_2d = slice(a, [0], [1, 2]); // 取第2列所有行,返回3×1列向量(二维张量)
row_2d = slice(a, [1, 2], [0]); // 取第2行所有列,返回1×3行向量(二维张量)
sub_matrix = slice(matrix, [0,3], [1,4]); // 行0-2,列1-3的子矩阵(二维张量)
// 2. 使用单个索引 - 返回一维张量
col_1d = slice(a, [0], 1); // 取第2列所有行,返回shape为[3]的一维张量
row_1d = slice(a, 1, [0]); // 取第2行所有列,返回shape为[3]的一维张量
// 实际应用示例
// 假设a是3×3矩阵:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
// 二维张量切片(保持矩阵结构)
col_vector = slice(a, [0], [1, 2]); // 返回[[2], [5], [8]] - 3×1列向量(二维张量)
row_vector = slice(a, [1, 2], [0]); // 返回[[4, 5, 6]] - 1×3行向量(二维张量)
// 一维张量切片(扁平化)
col_flat = slice(a, [0], 1); // 返回[2, 5, 8] - shape为[3]的一维张量
row_flat = slice(a, 1, [0]); // 返回[4, 5, 6] - shape为[3]的一维张量
// 电力系统应用示例
voltage = slice(bus, [0], [VM-1, VM]); // 所有行,VM列(二维张量)
angle = slice(bus, [0], VA-1); // 所有行,VA列(一维张量)
```
### 5.3 张量赋值操作
```rustscript
// assign函数用于对对应位置赋值,要求源张量和目标位置的形状匹配
// assign(target, source, row_spec, col_spec)
// 重要:source的维度必须与目标位置的维度匹配
// 1. 赋值二维张量 - source必须是匹配的二维张量
b_2d = [[1.0], [2.0], [3.0]]; // 3×1列向量(二维张量)
assign(a, b_2d, [0], [1, 2]); // 正确:b_2d是3×1列向量,匹配slice(a,[0],[1,2])
c_2d = [[1.0, 2.0, 3.0]]; // 1×3行向量(二维张量)
assign(a, c_2d, [1, 2], [0]); // 正确:c_2d是1×3行向量,匹配slice(a,[1,2],[0])
// 2. 赋值一维张量 - source必须是匹配的一维张量
b_1d = [1.0, 2.0, 3.0]; // shape为[3]的一维张量
assign(a, b_1d, [0], 1); // 正确:b_1d是一维张量,匹配slice(a,[0],1)
assign(a, b_1d, 1, [0]); // 正确:b_1d是一维张量,匹配slice(a,1,[0])
// 错误示例(形状不匹配)
// assign(a, b_1d, [0], [1, 2]); // 错误:一维张量不能赋值给二维位置
// assign(a, b_2d, [0], 1); // 错误:二维张量不能赋值给一维位置
// 实际应用示例
voltage = slice(bus, [0], [VM-1, VM]); // 获取电压幅值列(二维张量)
new_voltage = [[1.05], [1.02], [1.01]]; // 新的电压值(二维张量)
assign(bus, new_voltage, [0], [VM-1, VM]); // 更新电压幅值
angle_flat = slice(bus, [0], VA-1); // 获取电压角度(一维张量)
new_angles = [0.0, -2.5, 1.2]; // 新的角度值(一维张量)
assign(bus, new_angles, [0], VA-1); // 更新电压角度
```
### 5.4 张量形状和维度说明
```rustscript
// 重要概念区分:
a = [1, 2, 3]; // 一维张量, shape: [3]
b = [[1, 2, 3]]; // 行向量(二维张量), shape: [1, 3]
c = [[1], [2], [3]]; // 列向量(二维张量), shape: [3, 1]
// 在运算中:
// - 一维张量在运算时相当于行向量
// - 行向量和列向量都属于二维张量,但形状不同
// - slice和assign函数的行为取决于参数格式和返回的张量维度
// 形状检查示例
ndims(a); // 返回 1 (一维张量)
ndims(b); // 返回 2 (二维张量)
ndims(c); // 返回 2 (二维张量)
size(a, 0); // 返回 3
size(b, 0); // 返回 1 (行数)
size(b, 1); // 返回 3 (列数)
size(c, 0); // 返回 3 (行数)
size(c, 1); // 返回 1 (列数)
```
## 6. 运算符
### 6.1 算术运算符
```rustscript
// 标量运算
a + b; // 加法
a - b; // 减法
a * b; // 乘法
a / b; // 除法
a ^ b; // 幂运算
// 元素级运算(张量)
A .+ B; // 元素级加法
A .- B; // 元素级减法
A .* B; // 元素级乘法
A ./ B; // 元素级除法
A .^ 2; // 元素级幂运算
// 矩阵运算
A * B; // 矩阵乘法
A'; // 转置
inv(A); // 逆矩阵
det(A); // 行列式
```
### 6.2 比较运算符
```rustscript
// 标量比较
a == b; // 等于
a != b; // 不等于
a < b; // 小于
a <= b; // 小于等于
a > b; // 大于
a >= b; // 大于等于
// 元素级比较(张量)
A .== B; // 元素级等于
A .< B; // 元素级小于
```
### 6.3 逻辑运算符
```rustscript
// 标量逻辑
a && b; // 逻辑与
a || b; // 逻辑或
~~a; // 逻辑非(注意:是两个波浪号)
// 元素级逻辑(张量)
A .&& B; // 元素级逻辑与
A .|| B; // 元素级逻辑或
.~~A; // 元素级逻辑非(两个波浪号)
// 实际使用示例
bus_gen_status = ~~bus_gen_status; // 逻辑非运算
pq = find(bus_type == PQ || ~~bus_gen_status); // 组合逻辑运算
```
## 7. 函数定义
### 7.1 基本函数语法
```rustscript
fn function_name(param1, param2, ...) {
// 函数体
return result;
}
// 简化返回语法(最后一个表达式作为返回值)
fn add(a, b) {
a + b
}
```
**重要限制:RustScript函数只能返回一个变量,不支持多返回值。**
### 7.2 函数示例
```rustscript
fn make_y_bus(baseMVA, bus, branch) {
nb = size(bus, 0);
nl = size(branch, 0);
// 计算导纳矩阵
stat = slice(branch, [0], [BR_STATUS-1, BR_STATUS]);
Ys = stat ./ (slice(branch, [0], [BR_R-1, BR_R])
+ c(0,1) * slice(branch, [0], [BR_X-1, BR_X]));
return Ys; // 只能返回一个值
}
```
## 9. 内置函数
### 9.1 数学函数
```rustscript
// 基本数学函数
sqrt(x), exp(x), log(x), log10(x)
sin(x), cos(x), tan(x)
asin(x), acos(x), atan(x), atan2(y, x)
abs(x), sign(x), floor(x), ceil(x), round(x)
// 复数函数
real(z), imag(z), conj(z), abs(z), arg(z)
```
### 9.2 矩阵函数
```rustscript
// 线性代数
det(A), inv(A), rank(A), trace(A)
eig(A), svd(A), qr(A), chol(A)
norm(A), cond(A)
// 矩阵操作
transpose(A), diag(A), triu(A), tril(A)
reshape(A, m, n), repmat(A, m, n)
```
### 9.3 张量操作函数
```rustscript
// 形状和尺寸
size(tensor, dim) // 获取指定维度大小
length(tensor) // 获取元素总数
ndims(tensor) // 获取维度数
numel(tensor) // 元素个数
// 张量切片和操作
slice(tensor, row_range, col_range) // 张量切片操作
set(tensor, indices, values) // 设置张量元素值
set2(tensor, indices, values) // 张量元素累加赋值 (相当于 +=)
// 张量拼接
horzcat(A, B, ...) // 横向拼接张量
vertcat(A, B, ...) // 纵向拼接张量
// 序列生成
range(start, end) // 生成序列 [start, start+1, ..., end-1] (前闭后开)
linspace(start, end, num) // 在start和end间生成num个等间距点
// 统计函数
sum(tensor), mean(tensor), std(tensor), var(tensor)
max(tensor), min(tensor), median(tensor)
// 查找函数
find(condition) // 查找满足条件的索引
any(tensor), all(tensor)
// 获取多个元素
get_multi(tensor, indices) // 根据索引获取多个元素
```
### 9.4 稀疏矩阵
```rustscript
// 创建稀疏矩阵
sparse(row_indices, col_indices, values, m, n); // 创建m×n稀疏矩阵
// 稀疏矩阵操作
full_mat = full(sparse_mat); // 转为密集矩阵
nnz_count = nnz(sparse_mat); // 非零元素个数
spy(sparse_mat); // 显示稀疏模式
```
### 9.5 张量操作详细示例
```rustscript
// 实际使用示例(来自make_y_bus函数)
nb = size(bus, 0); // 获取母线数量
nl = size(branch, 0); // 获取支路数量
// 切片操作
stat = slice(branch, [0], [BR_STATUS-1, BR_STATUS]); // 获取支路状态列
Ys = stat ./ (slice(branch, [0], [BR_R-1, BR_R])
+ c(0,1) * slice(branch, [0], [BR_X-1, BR_X]));
// 查找和设置操作
index = find(tap_col); // 查找非零变比的索引
tap_init = set(ones(nl, 1), index, get_multi(tap_col, index));
// 序列生成和拼接
i = horzcat(range(0, nl), range(0, nl)) - 1; // 创建行索引
j = range(0, nb); // 创建列索引
// 稀疏矩阵创建
Yf = sparse(i, horzcat(f, t), upper, nl, nb);
Ybus = sparse(horzcat(f,f,t,t), horzcat(f,t,f,t),
vertcat(Yff,Yft,Ytf,Ytt), nb, nb);
```
## 10. 数据结构示例
### 10.1 电力系统数据(基于 case14)
```rustscript
// 系统基准功率
baseMVA = 100;
// 母线数据 - 每行必须用[]包围,数字用逗号分隔
bus = [
[1, 3, 0, 0, 0, 0, 1, 1.06, 0, 0, 1, 1.06, 0.94],
[2, 2, 21.7, 12.7, 0, 0, 1, 1.045, -4.98, 0, 1, 1.06, 0.94],
[3, 2, 94.2, 19, 0, 0, 1, 1.01, -12.72, 0, 1, 1.06, 0.94],
[4, 1, 47.8, -3.9, 0, 0, 1, 1.019, -10.33, 0, 1, 1.06, 0.94]
];
// 发电机数据
gen = [
[1, 232.4, -16.9, 10, 0, 1.06, 100, 1, 332.4, 0],
[2, 40, 42.4, 50, -40, 1.045, 100, 1, 140, 0],
[3, 0, 23.4, 40, 0, 1.01, 100, 1, 100, 0]
];
// 支路数据
branch = [
[1, 2, 0.01938, 0.05917, 0.0528, 0, 0, 0, 0, 0, 1, -360, 360],
[1, 5, 0.05403, 0.22304, 0.0492, 0, 0, 0, 0, 0, 1, -360, 360],
[2, 3, 0.04699, 0.19797, 0.0438, 0, 0, 0, 0, 0, 1, -360, 360]
];
```
## 11. 模块系统
### 11.1 模块导入
```rustscript
// 导入整个模块
import power_flow;
// 导入特定函数
import {newton_pf, fast_decoupled_pf} from power_flow;
// 别名导入
import newton_pf as newton from power_flow;
```
### 11.2 模块定义
```rustscript
// 模块文件: power_flow.rs
export fn newton_pf(baseMVA, bus, gen, branch) {
// 实现牛顿拉夫逊潮流计算
}
export fn fast_decoupled_pf(baseMVA, bus, gen, branch) {
// 实现快速解耦潮流计算
}
```
## 12. 错误处理
### 12.1 Result 类型
```rustscript
fn divide(a, b) -> Result<f64, String> {
if b == 0.0 {
return Err("Division by zero");
}
Ok(a / b)
}
// 使用
result = divide(10.0, 2.0);
match result {
Ok(value) => println("Result: {}", value),
Err(error) => println("Error: {}", error),
}
```
### 12.2 异常处理
```rustscript
try {
result = risky_operation();
} catch (error) {
println("Caught error: {}", error);
result = default_value;
}
```
## 13. 与MATLAB的对比
### 13.1 主要差异
| 特性 | MATLAB | RustScript | 说明 |
|------|---------------------|-----------------------|------|
| 索引基数 | 1基索引 `A(1,2)` | 0基索引 `A[0,1]` | **重要差异:索引从不同位置开始** |
| 矩阵定义 | `[1 2; 3 4]` | `[[1, 2], [3, 4]]` | 分号分行,逗号分元素 |
| 序列生成 | `0:n-1` | `range(0, n)` | MATLAB包含端点,RustScript前闭后开 |
| 注释 | `% 注释` | `// 注释` | 注释符号不同 |
| 字符串 | `'string'` | `"string"` | 引号类型不同 |
| 函数定义 | `function y = f(x)` | `fn f(x){ return y;}` | 函数语法不同 |
| 逻辑运算 | `&`, `\|`, `~` | `&&`, `\|\|`, `~~` | 逻辑运算符不同 |
### 13.2 索引转换详解
**最重要的差异:MATLAB使用1基索引,RustScript使用0基索引**
```matlab
% MATLAB (1基索引)
A(1, 1) % 第一个元素
A(1:3, 2) % 第1到3行,第2列
bus(:, i) % 第i列所有行
0:n-1 % 生成 [0, 1, 2, ..., n-1]
```
```rustscript
// RustScript (0基索引)
A[0, 0] // 第一个元素
slice(A, [0,3], [1]) // 第1到3行(0,1,2),第2列(索引1)
slice(bus, [0], [i-1,i]) // 第i列所有行(需要减1)
range(0, n) // 生成 [0, 1, 2, ..., n-1] (前闭后开)
```
### 13.3 转换示例对比
**MATLAB到RustScript的典型转换:**
```matlab
% MATLAB版本
function Ybus = make_y_bus(baseMVA, bus, branch)
nb = size(bus, 1); % 获取行数
f = branch(:, F_BUS); % 从母线
t = branch(:, T_BUS); % 到母线
% 创建索引(1基)
i = [1:nl, 1:nl];
j = 1:nb;
% 稀疏矩阵(MATLAB自动处理1基索引)
Ybus = sparse(i, j, values, nb, nb);
end
```
```rustscript
// RustScript版本
fn make_y_bus(baseMVA, bus, branch) {
nb = size(bus, 0); // 获取行数
f = slice(branch, [0], [F_BUS-1]) - 1; // 从母线(转换为0基)
t = slice(branch, [0], [T_BUS-1]) - 1; // 到母线(转换为0基)
// 创建索引(0基)
i = horzcat(range(0, nl), range(0, nl));
j = range(0, nb);
// 稀疏矩阵(显式使用0基索引)
Ybus = sparse(i, j, values, nb, nb);
return Ybus;
}
```
### 13.4 转换器的索引处理策略
在MATLAB到RustScript转换过程中,转换器需要特别处理索引:
**Case文件转换(保持原始索引):**
- Case文件通常包含数据定义,索引值保持不变
- 例如:母线编号仍然是1, 2, 3...
**函数转换(转换为0基索引):**
- 函数中的索引操作需要转换:`A(i)``A[i-1]`
- 循环变量需要调整:`for i = 1:n``for i in range(0, n)`
- 序列生成需要转换:`1:n``range(1, n+1)``range(0, n)`
# MATLAB到RustScript格式转换工具
## 项目概述
本项目旨在实现一个将MATLAB格式的电力系统数据文件(如case14.m)转换为RustScript格式文件(如case14.txt)的转换工具。
## 输入输出格式对比
### 输入格式(MATLAB .m文件)
- **文件结构**:MATLAB函数定义,包含结构体字段
- **注释格式**:以 `%` 开头的注释
- **函数定义**`function mpc = case14`
- **矩阵格式**:使用分号 `;` 分隔行,空格或制表符分隔列
- **数据组织**:通过结构体字段组织(如 `mpc.bus`, `mpc.gen`
### 输出格式(RustScript .txt文件)
- **文件结构**:直接的数据定义,无函数包装
- **注释格式**:以 `//` 开头的注释
- **函数定义**:转换为 `fn` 关键字(如需要)
- **矩阵格式**:每行用方括号 `[]` 包围,元素间用逗号 `,` 分隔,**支持列对齐**
- **数据组织**:直接变量赋值(如 `bus = [...]`
## 具体转换规则
### 1. 注释转换
- **输入**`%CASE14 Power flow data for IEEE 14 bus test case.`
- **输出**`//CASE14 Power flow data for IEEE 14 bus test case.`
### 2. 函数定义转换
- **输入**`function mpc = case14`
- **输出**`fn case14() -> MPC` 或直接省略(根据需求)
### 3. 结构体字段转换
- **输入**`mpc.baseMVA = 100;`
- **输出**`baseMVA = 100;`
### 4. 矩阵格式转换(支持列对齐)
**输入(MATLAB格式):**
```matlab
mpc.bus = [
1 3 0 0 0 0 1 1.06 0 0 1 1.06 0.94;
2 2 21.7 12.7 0 0 1 1.045 -4.98 0 1 1.06 0.94;
3 2 94.2 19 0 0 1 1.01 -12.72 0 1 1.06 0.94;
];
```
**输出(RustScript格式,列对齐):**
```rust
bus = [
[ 1, 3, 0, 0, 0, 0, 1, 1.06, 0, 0, 1, 1.06, 0.94],
[ 2, 2, 21.7, 12.7, 0, 0, 1, 1.045, -4.98, 0, 1, 1.06, 0.94],
[ 3, 2, 94.2, 19, 0, 0, 1, 1.01, -12.72, 0, 1, 1.06, 0.94]
];
```
### 5. 特殊结构处理
**bus_name字段(字符串数组):**
- **输入**
```matlab
mpc.bus_name = {
'Bus 1 HV';
'Bus 2 HV';
};
```
- **输出**
```rust
bus_name = [
"Bus 1 HV",
"Bus 2 HV"
];
```
## 实现架构
### 核心模块
1. **词法分析器**(lexer.rs):将MATLAB代码分解为tokens
2. **语法分析器**(parser.rs):解析MATLAB语法结构
3. **转换器模块**(converter.rs):执行语法转换规则
4. **代码生成器**(codegen.rs):生成格式化的RustScript代码
### 数据结构设计(保持代码顺序)
为了保持原有代码的顺序并支持后续扩展,我们使用基于AST(抽象语法树)的方法:
```rust
#[derive(Debug, Clone)]
pub enum MATLABNode {
Comment(String),
FunctionDef {
name: String,
output_var: String,
},
Assignment {
target: String,
value: MATLABValue,
},
BlankLine,
SectionHeader(String),
}
#[derive(Debug, Clone)]
pub enum MATLABValue {
Scalar(f64),
String(String),
Matrix(Vec<Vec<MATLABValue>>),
CellArray(Vec<MATLABValue>),
StructField {
object: String,
field: String,
value: Box<MATLABValue>,
},
}
#[derive(Debug)]
pub struct MATLABDocument {
pub nodes: Vec<MATLABNode>,
}
```
### 转换流程
1. **词法分析**:将MATLAB代码分解为tokens
2. **语法分析**:构建AST(抽象语法树)
3. **转换处理**:遍历AST,应用转换规则
4. **代码生成**:输出格式化的RustScript代码(支持列对齐)
### 核心特性
- **保持顺序**:AST节点按原始代码顺序存储
- **易于扩展**:新的MATLAB表达式类型只需添加新的枚举变体
- **灵活转换**:可以对每个节点应用不同的转换规则
- **列对齐支持**:矩阵输出时自动计算列宽并对齐
- **调试友好**:可以追踪每个节点的源位置
## 使用方式
### 命令行接口
```bash
# 转换单个文件
cargo run -- input.m output.txt
# 使用默认case14文件测试
cargo run
# 示例:转换case14.m
cargo run -- ../rspower/data/case14.m ../rspower/data/case14_converted.txt
```
### 编程接口
```rust
use rustscript_transpiler::MATLABToRustConverter;
let converter = MATLABToRustConverter::new();
let result = converter.convert_file("case14.m", "case14.txt")?;
```
## 测试策略
### 单元测试
- 各个转换规则的独立测试
- 边界情况处理测试
- 错误处理测试
### 集成测试
- 完整文件转换测试
- 多种MATLAB格式支持测试
- 输出格式验证测试
### 验证测试
- 转换后数据的数值准确性验证
- 格式兼容性测试
- 列对齐功能测试
- 性能测试
## 错误处理
### 常见错误类型
1. **语法错误**:无法解析的MATLAB语法
2. **数据格式错误**:不支持的数据类型
3. **文件IO错误**:文件读写失败
4. **转换错误**:转换过程中的逻辑错误
### 错误恢复策略
1. 提供详细的错误信息和位置
2. 支持部分转换和警告输出
3. 提供手动干预选项
## 扩展性考虑
### 支持更多MATLAB特性
- 更复杂的数据结构
- 嵌套矩阵和单元格数组
- 更多的数据类型支持
### 配置化转换规则
- 允许用户自定义转换规则
- 支持不同的输出格式
- 插件式架构支持
## 项目文件结构
```
rustscript-transpiler/
├── Cargo.toml # 项目配置文件
├── LANGUAGE_SPEC.md # 语言规范文档
├── MATLAB_TO_RUST_CONVERTER.md # 项目文档(本文档)
├── src/
│ ├── lib.rs # 库入口,协调各模块
│ ├── main.rs # 主程序入口
│ ├── ast.rs # AST节点定义
│ ├── lexer.rs # 词法分析器
│ ├── parser.rs # 语法分析器
│ ├── converter.rs # 转换器逻辑
│ └── codegen.rs # 代码生成器(支持列对齐)
├── tests/
│ └── integration_tests.rs # 集成测试
└── examples/
└── convert_case.rs # 转换case的示例
```
## 核心功能特性
### 1. 列对齐支持
- **自动计算列宽**:扫描整个矩阵,计算每列的最大宽度
- **右对齐数字**:数字采用右对齐格式,提高可读性
- **智能格式化**:自动选择合适的数字精度和格式
### 2. 数字格式化
- **整数处理**:整数显示为整数格式
- **小数处理**:自动去除尾部零
- **科学计数法**:极大或极小数字使用科学计数法
### 3. 代码顺序保持
- **AST结构**:按原始文件顺序存储节点
- **逐行处理**:保持原始代码的逻辑顺序
- **注释保持**:保留原始注释的位置
## 开发计划
### 第一阶段:基础转换功能 ✅
- [x] 项目文档完成
- [x] 词法分析器实现
- [x] 基础语法分析器实现
- [x] 核心转换逻辑实现
- [x] 基本测试用例
- [x] 列对齐功能实现
### 第二阶段:完善和优化
- [ ] 错误处理完善
- [ ] 性能优化
- [ ] 更多MATLAB语法支持
- [ ] 扩展功能支持
- [ ] 文档和示例完善
### 第三阶段:生产就绪
- [ ] 全面测试
- [ ] 用户界面优化
- [ ] 部署和发布准备
- [ ] 持续集成配置
### 后续计划
- [ ] 支持加减乘除等多种运算的读取
- [ ] 支持矩阵切片,赋值等操作(采用converter)
- [ ] 支持多种内置函数的解析和转换
- 写测试
function mpc = case14
%CASE14 Power flow data for IEEE 14 bus test case.
% Please see CASEFORMAT for details on the case file format.
% This data was converted from IEEE Common Data Format
% (ieee14cdf.txt) on 15-Oct-2014 by cdf2matp, rev. 2393
% See end of file for warnings generated during conversion.
%
% Converted from IEEE CDF file from:
% https://labs.ece.uw.edu/pstca/
%
% 08/19/93 UW ARCHIVE 100.0 1962 W IEEE 14 Bus Test Case
% MATPOWER
%% MATPOWER Case Format : Version 2
mpc.version = '2';
%%----- Power Flow Data -----%%
%% system MVA base
mpc.baseMVA = 100;
%% bus data
% bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin
mpc.bus = [
1 3 0 0 0 0 1 1.06 0 0 1 1.06 0.94;
2 2 21.7 12.7 0 0 1 1.045 -4.98 0 1 1.06 0.94;
3 2 94.2 19 0 0 1 1.01 -12.72 0 1 1.06 0.94;
4 1 47.8 -3.9 0 0 1 1.019 -10.33 0 1 1.06 0.94;
5 1 7.6 1.6 0 0 1 1.02 -8.78 0 1 1.06 0.94;
6 2 11.2 7.5 0 0 1 1.07 -14.22 0 1 1.06 0.94;
7 1 0 0 0 0 1 1.062 -13.37 0 1 1.06 0.94;
8 2 0 0 0 0 1 1.09 -13.36 0 1 1.06 0.94;
9 1 29.5 16.6 0 19 1 1.056 -14.94 0 1 1.06 0.94;
10 1 9 5.8 0 0 1 1.051 -15.1 0 1 1.06 0.94;
11 1 3.5 1.8 0 0 1 1.057 -14.79 0 1 1.06 0.94;
12 1 6.1 1.6 0 0 1 1.055 -15.07 0 1 1.06 0.94;
13 1 13.5 5.8 0 0 1 1.05 -15.16 0 1 1.06 0.94;
14 1 14.9 5 0 0 1 1.036 -16.04 0 1 1.06 0.94;
];
%% generator data
% bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin Pc1 Pc2 Qc1min Qc1max Qc2min Qc2max ramp_agc ramp_10 ramp_30 ramp_q apf
mpc.gen = [
1 232.4 -16.9 10 0 1.06 100 1 332.4 0 0 0 0 0 0 0 0 0 0 0 0;
2 40 42.4 50 -40 1.045 100 1 140 0 0 0 0 0 0 0 0 0 0 0 0;
3 0 23.4 40 0 1.01 100 1 100 0 0 0 0 0 0 0 0 0 0 0 0;
6 0 12.2 24 -6 1.07 100 1 100 0 0 0 0 0 0 0 0 0 0 0 0;
8 0 17.4 24 -6 1.09 100 1 100 0 0 0 0 0 0 0 0 0 0 0 0;
];
%% branch data
% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
mpc.branch = [
1 2 0.01938 0.05917 0.0528 0 0 0 0 0 1 -360 360;
1 5 0.05403 0.22304 0.0492 0 0 0 0 0 1 -360 360;
2 3 0.04699 0.19797 0.0438 0 0 0 0 0 1 -360 360;
2 4 0.05811 0.17632 0.034 0 0 0 0 0 1 -360 360;
2 5 0.05695 0.17388 0.0346 0 0 0 0 0 1 -360 360;
3 4 0.06701 0.17103 0.0128 0 0 0 0 0 1 -360 360;
4 5 0.01335 0.04211 0 0 0 0 0 0 1 -360 360;
4 7 0 0.20912 0 0 0 0 0.978 0 1 -360 360;
4 9 0 0.55618 0 0 0 0 0.969 0 1 -360 360;
5 6 0 0.25202 0 0 0 0 0.932 0 1 -360 360;
6 11 0.09498 0.1989 0 0 0 0 0 0 1 -360 360;
6 12 0.12291 0.25581 0 0 0 0 0 0 1 -360 360;
6 13 0.06615 0.13027 0 0 0 0 0 0 1 -360 360;
7 8 0 0.17615 0 0 0 0 0 0 1 -360 360;
7 9 0 0.11001 0 0 0 0 0 0 1 -360 360;
9 10 0.03181 0.0845 0 0 0 0 0 0 1 -360 360;
9 14 0.12711 0.27038 0 0 0 0 0 0 1 -360 360;
10 11 0.08205 0.19207 0 0 0 0 0 0 1 -360 360;
12 13 0.22092 0.19988 0 0 0 0 0 0 1 -360 360;
13 14 0.17093 0.34802 0 0 0 0 0 0 1 -360 360;
];
%%----- OPF Data -----%%
%% generator cost data
% 1 startup shutdown n x1 y1 ... xn yn
% 2 startup shutdown n c(n-1) ... c0
mpc.gencost = [
2 0 0 3 0.0430292599 20 0;
2 0 0 3 0.25 20 0;
2 0 0 3 0.01 40 0;
2 0 0 3 0.01 40 0;
2 0 0 3 0.01 40 0;
];
%% bus names
mpc.bus_name = {
'Bus 1 HV';
'Bus 2 HV';
'Bus 3 HV';
'Bus 4 HV';
'Bus 5 HV';
'Bus 6 LV';
'Bus 7 ZV';
'Bus 8 TV';
'Bus 9 LV';
'Bus 10 LV';
'Bus 11 LV';
'Bus 12 LV';
'Bus 13 LV';
'Bus 14 LV';
};
% Warnings from cdf2matp conversion:
%
% ***** check the title format in the first line of the cdf file.
% ***** Qmax = Qmin at generator at bus 1 (Qmax set to Qmin + 10)
% ***** MVA limit of branch 1 - 2 not given, set to 0
% ***** MVA limit of branch 1 - 5 not given, set to 0
% ***** MVA limit of branch 2 - 3 not given, set to 0
% ***** MVA limit of branch 2 - 4 not given, set to 0
% ***** MVA limit of branch 2 - 5 not given, set to 0
% ***** MVA limit of branch 3 - 4 not given, set to 0
% ***** MVA limit of branch 4 - 5 not given, set to 0
% ***** MVA limit of branch 4 - 7 not given, set to 0
% ***** MVA limit of branch 4 - 9 not given, set to 0
% ***** MVA limit of branch 5 - 6 not given, set to 0
% ***** MVA limit of branch 6 - 11 not given, set to 0
% ***** MVA limit of branch 6 - 12 not given, set to 0
% ***** MVA limit of branch 6 - 13 not given, set to 0
% ***** MVA limit of branch 7 - 8 not given, set to 0
% ***** MVA limit of branch 7 - 9 not given, set to 0
% ***** MVA limit of branch 9 - 10 not given, set to 0
% ***** MVA limit of branch 9 - 14 not given, set to 0
% ***** MVA limit of branch 10 - 11 not given, set to 0
% ***** MVA limit of branch 12 - 13 not given, set to 0
% ***** MVA limit of branch 13 - 14 not given, set to 0
fn case14() -> mpc {
//CASE14 Power flow data for IEEE 14 bus test case.
// Please see CASEFORMAT for details on the case file format.
// This data was converted from IEEE Common Data Format
// (ieee14cdf.txt) on 15-Oct-2014 by cdf2matp, rev. 2393
// See end of file for warnings generated during conversion.
//
// Converted from IEEE CDF file from:
// https://labs.ece.uw.edu/pstca/
//
// 08/19/93 UW ARCHIVE 100.0 1962 W IEEE 14 Bus Test Case
// MATPOWER
//% MATPOWER Case Format : Version 2
version = "2";
//%----- Power Flow Data -----%%
//% system MVA base
baseMVA = 100;
//% bus data
// bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin
bus = [
[ 1, 3, 0, 0, 0, 0, 1, 1.06, 0, 0, 1, 1.06, 0.94],
[ 2, 2, 21.7, 12.7, 0, 0, 1, 1.045, -4.98, 0, 1, 1.06, 0.94],
[ 3, 2, 94.2, 19, 0, 0, 1, 1.01, -12.72, 0, 1, 1.06, 0.94],
[ 4, 1, 47.8, -3.9, 0, 0, 1, 1.019, -10.33, 0, 1, 1.06, 0.94],
[ 5, 1, 7.6, 1.6, 0, 0, 1, 1.02, -8.78, 0, 1, 1.06, 0.94],
[ 6, 2, 11.2, 7.5, 0, 0, 1, 1.07, -14.22, 0, 1, 1.06, 0.94],
[ 7, 1, 0, 0, 0, 0, 1, 1.062, -13.37, 0, 1, 1.06, 0.94],
[ 8, 2, 0, 0, 0, 0, 1, 1.09, -13.36, 0, 1, 1.06, 0.94],
[ 9, 1, 29.5, 16.6, 0, 19, 1, 1.056, -14.94, 0, 1, 1.06, 0.94],
[10, 1, 9, 5.8, 0, 0, 1, 1.051, -15.1, 0, 1, 1.06, 0.94],
[11, 1, 3.5, 1.8, 0, 0, 1, 1.057, -14.79, 0, 1, 1.06, 0.94],
[12, 1, 6.1, 1.6, 0, 0, 1, 1.055, -15.07, 0, 1, 1.06, 0.94],
[13, 1, 13.5, 5.8, 0, 0, 1, 1.05, -15.16, 0, 1, 1.06, 0.94],
[14, 1, 14.9, 5, 0, 0, 1, 1.036, -16.04, 0, 1, 1.06, 0.94]
];
//% generator data
// bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin Pc1 Pc2 Qc1min Qc1max Qc2min Qc2max ramp_agc ramp_10 ramp_30 ramp_q apf
gen = [
[1, 232.4, -16.9, 10, 0, 1.06, 100, 1, 332.4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 40, 42.4, 50, -40, 1.045, 100, 1, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 0, 23.4, 40, 0, 1.01, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[6, 0, 12.2, 24, -6, 1.07, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[8, 0, 17.4, 24, -6, 1.09, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
//% branch data
// fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
branch = [
[ 1, 2, 0.01938, 0.05917, 0.0528, 0, 0, 0, 0, 0, 1, -360, 360],
[ 1, 5, 0.05403, 0.22304, 0.0492, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 3, 0.04699, 0.19797, 0.0438, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 4, 0.05811, 0.17632, 0.034, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 5, 0.05695, 0.17388, 0.0346, 0, 0, 0, 0, 0, 1, -360, 360],
[ 3, 4, 0.06701, 0.17103, 0.0128, 0, 0, 0, 0, 0, 1, -360, 360],
[ 4, 5, 0.01335, 0.04211, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 4, 7, 0, 0.20912, 0, 0, 0, 0, 0.978, 0, 1, -360, 360],
[ 4, 9, 0, 0.55618, 0, 0, 0, 0, 0.969, 0, 1, -360, 360],
[ 5, 6, 0, 0.25202, 0, 0, 0, 0, 0.932, 0, 1, -360, 360],
[ 6, 11, 0.09498, 0.1989, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 6, 12, 0.12291, 0.25581, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 6, 13, 0.06615, 0.13027, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 7, 8, 0, 0.17615, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 7, 9, 0, 0.11001, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 9, 10, 0.03181, 0.0845, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 9, 14, 0.12711, 0.27038, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[10, 11, 0.08205, 0.19207, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[12, 13, 0.22092, 0.19988, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[13, 14, 0.17093, 0.34802, 0, 0, 0, 0, 0, 0, 1, -360, 360]
];
//%----- OPF Data -----%%
//% generator cost data
// 1 startup shutdown n x1 y1 ... xn yn
// 2 startup shutdown n c(n-1) ... c0
gencost = [
[2, 0, 0, 3, 0.0430292599, 20, 0],
[2, 0, 0, 3, 0.25, 20, 0],
[2, 0, 0, 3, 0.01, 40, 0],
[2, 0, 0, 3, 0.01, 40, 0],
[2, 0, 0, 3, 0.01, 40, 0]
];
//% bus names
bus_name = [
"Bus 1 HV",
"Bus 2 HV",
"Bus 3 HV",
"Bus 4 HV",
"Bus 5 HV",
"Bus 6 LV",
"Bus 7 ZV",
"Bus 8 TV",
"Bus 9 LV",
"Bus 10 LV",
"Bus 11 LV",
"Bus 12 LV",
"Bus 13 LV",
"Bus 14 LV"
];
// Warnings from cdf2matp conversion:
//
// ***** check the title format in the first line of the cdf file.
// ***** Qmax = Qmin at generator at bus 1 (Qmax set to Qmin + 10)
// ***** MVA limit of branch 1 - 2 not given, set to 0
// ***** MVA limit of branch 1 - 5 not given, set to 0
// ***** MVA limit of branch 2 - 3 not given, set to 0
// ***** MVA limit of branch 2 - 4 not given, set to 0
// ***** MVA limit of branch 2 - 5 not given, set to 0
// ***** MVA limit of branch 3 - 4 not given, set to 0
// ***** MVA limit of branch 4 - 5 not given, set to 0
// ***** MVA limit of branch 4 - 7 not given, set to 0
// ***** MVA limit of branch 4 - 9 not given, set to 0
// ***** MVA limit of branch 5 - 6 not given, set to 0
// ***** MVA limit of branch 6 - 11 not given, set to 0
// ***** MVA limit of branch 6 - 12 not given, set to 0
// ***** MVA limit of branch 6 - 13 not given, set to 0
// ***** MVA limit of branch 7 - 8 not given, set to 0
// ***** MVA limit of branch 7 - 9 not given, set to 0
// ***** MVA limit of branch 9 - 10 not given, set to 0
// ***** MVA limit of branch 9 - 14 not given, set to 0
// ***** MVA limit of branch 10 - 11 not given, set to 0
// ***** MVA limit of branch 12 - 13 not given, set to 0
// ***** MVA limit of branch 13 - 14 not given, set to 0
fn case14() -> mpc {
//CASE14 Power flow data for IEEE 14 bus test case.
// Please see CASEFORMAT for details on the case file format.
// This data was converted from IEEE Common Data Format
// (ieee14cdf.txt) on 15-Oct-2014 by cdf2matp, rev. 2393
// See end of file for warnings generated during conversion.
//
// Converted from IEEE CDF file from:
// https://labs.ece.uw.edu/pstca/
//
// 08/19/93 UW ARCHIVE 100.0 1962 W IEEE 14 Bus Test Case
// MATPOWER
//% MATPOWER Case Format : Version 2
version = "2";
//%----- Power Flow Data -----%%
//% system MVA base
baseMVA = 100;
//% bus data
// bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin
bus = [
[ 1, 3, 0, 0, 0, 0, 1, 1.06, 0, 0, 1, 1.06, 0.94],
[ 2, 2, 21.7, 12.7, 0, 0, 1, 1.045, -4.98, 0, 1, 1.06, 0.94],
[ 3, 2, 94.2, 19, 0, 0, 1, 1.01, -12.72, 0, 1, 1.06, 0.94],
[ 4, 1, 47.8, -3.9, 0, 0, 1, 1.019, -10.33, 0, 1, 1.06, 0.94],
[ 5, 1, 7.6, 1.6, 0, 0, 1, 1.02, -8.78, 0, 1, 1.06, 0.94],
[ 6, 2, 11.2, 7.5, 0, 0, 1, 1.07, -14.22, 0, 1, 1.06, 0.94],
[ 7, 1, 0, 0, 0, 0, 1, 1.062, -13.37, 0, 1, 1.06, 0.94],
[ 8, 2, 0, 0, 0, 0, 1, 1.09, -13.36, 0, 1, 1.06, 0.94],
[ 9, 1, 29.5, 16.6, 0, 19, 1, 1.056, -14.94, 0, 1, 1.06, 0.94],
[10, 1, 9, 5.8, 0, 0, 1, 1.051, -15.1, 0, 1, 1.06, 0.94],
[11, 1, 3.5, 1.8, 0, 0, 1, 1.057, -14.79, 0, 1, 1.06, 0.94],
[12, 1, 6.1, 1.6, 0, 0, 1, 1.055, -15.07, 0, 1, 1.06, 0.94],
[13, 1, 13.5, 5.8, 0, 0, 1, 1.05, -15.16, 0, 1, 1.06, 0.94],
[14, 1, 14.9, 5, 0, 0, 1, 1.036, -16.04, 0, 1, 1.06, 0.94]
];
//% generator data
// bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin Pc1 Pc2 Qc1min Qc1max Qc2min Qc2max ramp_agc ramp_10 ramp_30 ramp_q apf
gen = [
[1, 232.4, -16.9, 10, 0, 1.06, 100, 1, 332.4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 40, 42.4, 50, -40, 1.045, 100, 1, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 0, 23.4, 40, 0, 1.01, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[6, 0, 12.2, 24, -6, 1.07, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[8, 0, 17.4, 24, -6, 1.09, 100, 1, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
//% branch data
// fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
branch = [
[ 1, 2, 0.01938, 0.05917, 0.0528, 0, 0, 0, 0, 0, 1, -360, 360],
[ 1, 5, 0.05403, 0.22304, 0.0492, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 3, 0.04699, 0.19797, 0.0438, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 4, 0.05811, 0.17632, 0.034, 0, 0, 0, 0, 0, 1, -360, 360],
[ 2, 5, 0.05695, 0.17388, 0.0346, 0, 0, 0, 0, 0, 1, -360, 360],
[ 3, 4, 0.06701, 0.17103, 0.0128, 0, 0, 0, 0, 0, 1, -360, 360],
[ 4, 5, 0.01335, 0.04211, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 4, 7, 0, 0.20912, 0, 0, 0, 0, 0.978, 0, 1, -360, 360],
[ 4, 9, 0, 0.55618, 0, 0, 0, 0, 0.969, 0, 1, -360, 360],
[ 5, 6, 0, 0.25202, 0, 0, 0, 0, 0.932, 0, 1, -360, 360],
[ 6, 11, 0.09498, 0.1989, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 6, 12, 0.12291, 0.25581, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 6, 13, 0.06615, 0.13027, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 7, 8, 0, 0.17615, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 7, 9, 0, 0.11001, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 9, 10, 0.03181, 0.0845, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[ 9, 14, 0.12711, 0.27038, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[10, 11, 0.08205, 0.19207, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[12, 13, 0.22092, 0.19988, 0, 0, 0, 0, 0, 0, 1, -360, 360],
[13, 14, 0.17093, 0.34802, 0, 0, 0, 0, 0, 0, 1, -360, 360]
];
//%----- OPF Data -----%%
//% generator cost data
// 1 startup shutdown n x1 y1 ... xn yn
// 2 startup shutdown n c(n-1) ... c0
gencost = [
[2, 0, 0, 3, 0.0430292599, 20, 0],
[2, 0, 0, 3, 0.25, 20, 0],
[2, 0, 0, 3, 0.01, 40, 0],
[2, 0, 0, 3, 0.01, 40, 0],
[2, 0, 0, 3, 0.01, 40, 0]
];
//% bus names
bus_name = [
"Bus 1 HV",
"Bus 2 HV",
"Bus 3 HV",
"Bus 4 HV",
"Bus 5 HV",
"Bus 6 LV",
"Bus 7 ZV",
"Bus 8 TV",
"Bus 9 LV",
"Bus 10 LV",
"Bus 11 LV",
"Bus 12 LV",
"Bus 13 LV",
"Bus 14 LV"
];
// Warnings from cdf2matp conversion:
//
// ***** check the title format in the first line of the cdf file.
// ***** Qmax = Qmin at generator at bus 1 (Qmax set to Qmin + 10)
// ***** MVA limit of branch 1 - 2 not given, set to 0
// ***** MVA limit of branch 1 - 5 not given, set to 0
// ***** MVA limit of branch 2 - 3 not given, set to 0
// ***** MVA limit of branch 2 - 4 not given, set to 0
// ***** MVA limit of branch 2 - 5 not given, set to 0
// ***** MVA limit of branch 3 - 4 not given, set to 0
// ***** MVA limit of branch 4 - 5 not given, set to 0
// ***** MVA limit of branch 4 - 7 not given, set to 0
// ***** MVA limit of branch 4 - 9 not given, set to 0
// ***** MVA limit of branch 5 - 6 not given, set to 0
// ***** MVA limit of branch 6 - 11 not given, set to 0
// ***** MVA limit of branch 6 - 12 not given, set to 0
// ***** MVA limit of branch 6 - 13 not given, set to 0
// ***** MVA limit of branch 7 - 8 not given, set to 0
// ***** MVA limit of branch 7 - 9 not given, set to 0
// ***** MVA limit of branch 9 - 10 not given, set to 0
// ***** MVA limit of branch 9 - 14 not given, set to 0
// ***** MVA limit of branch 10 - 11 not given, set to 0
// ***** MVA limit of branch 12 - 13 not given, set to 0
// ***** MVA limit of branch 13 - 14 not given, set to 0
use rustscript_transpiler::MATLABToRustConverter;
use std::env;
use std::process;
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
// 如果没有参数,使用默认的case14文件进行测试
if args.len() == 1 {
println!("未提供参数,使用默认case14文件进行测试...");
test_case14_conversion();
return;
}
if args.len() != 3 {
eprintln!("用法: {} <input.m> <output.txt>", args[0]);
eprintln!("示例: {} case14.m case14_rustscript.txt", args[0]);
eprintln!("或者直接运行 {} 来测试case14转换", args[0]);
process::exit(1);
}
let input_file = &args[1];
let output_file = &args[2];
println!("正在转换 {} 到 {}", input_file, output_file);
let converter = MATLABToRustConverter::new();
match converter.convert_file(input_file, output_file) {
Ok(()) => {
println!("✅ 转换成功完成!");
}
Err(e) => {
eprintln!("❌ 转换失败: {}", e);
process::exit(1);
}
}
}
fn test_case14_conversion() {
let input_file = r"./rustscript-transpiler/cases/case14.m";
let output_file = r"./rustscript-transpiler/cases/case14_converted.txt";
println!("🔄 开始转换 case14.m 文件...");
println!("输入文件: {}", input_file);
println!("输出文件: {}", output_file);
let converter = MATLABToRustConverter::new();
match converter.convert_file(input_file, output_file) {
Ok(()) => {
println!("✅ 转换成功完成!");
}
Err(e) => {
println!("❌ 转换失败: {}", e);
}
}
}
/// MATLAB抽象语法树节点定义
///
/// 这个设计的关键是保持原始代码的顺序,每个节点都按照在源文件中出现的顺序存储
#[derive(Debug, Clone)]
pub enum MATLABNode {
/// 注释行
Comment(String),
/// 函数定义
FunctionDef {
name: String,
output_var: String,
},
/// 赋值语句
Assignment {
target: String,
value: MATLABValue,
},
/// 空行
BlankLine,
/// 章节分隔符(如 %%----- Power Flow Data -----%%)
SectionHeader(String),
}
/// MATLAB值的类型
#[derive(Debug, Clone)]
pub enum MATLABValue {
/// 标量值
Scalar(f64),
/// 字符串
String(String),
/// 矩阵(二维数组)
Matrix(Vec<Vec<MATLABValue>>),
/// 单元格数组
CellArray(Vec<MATLABValue>),
/// 结构体字段赋值
StructField {
object: String,
field: String,
value: Box<MATLABValue>,
},
}
/// 完整的MATLAB文档AST
#[derive(Debug)]
pub struct MATLABDocument {
/// 按原始顺序存储的节点列表
pub nodes: Vec<MATLABNode>,
}
impl MATLABDocument {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
}
}
pub fn add_node(&mut self, node: MATLABNode) {
self.nodes.push(node);
}
/// 获取所有注释
pub fn get_comments(&self) -> Vec<&String> {
self.nodes.iter().filter_map(|node| {
if let MATLABNode::Comment(comment) = node {
Some(comment)
} else {
None
}
}).collect()
}
/// 获取函数定义
pub fn get_function_def(&self) -> Option<(&String, &String)> {
self.nodes.iter().find_map(|node| {
if let MATLABNode::FunctionDef { name, output_var } = node {
Some((name, output_var))
} else {
None
}
})
}
/// 获取所有赋值语句
pub fn get_assignments(&self) -> Vec<(&String, &MATLABValue)> {
self.nodes.iter().filter_map(|node| {
if let MATLABNode::Assignment { target, value } = node {
Some((target, value))
} else {
None
}
}).collect()
}
}
impl Default for MATLABDocument {
fn default() -> Self {
Self::new()
}
}
/// 源代码位置信息,用于错误报告和调试
#[derive(Debug, Clone)]
pub struct SourceLocation {
pub line: usize,
pub column: usize,
}
/// 带有位置信息的节点,用于调试
#[derive(Debug, Clone)]
pub struct LocatedNode {
pub node: MATLABNode,
pub location: SourceLocation,
}
use crate::ast::{MATLABDocument, MATLABNode, MATLABValue};
use std::io::Result;
/// 代码生成器,负责将转换后的AST生成为RustScript代码
pub struct CodeGenerator {
indent_level: usize,
indent_size: usize,
}
impl CodeGenerator {
pub fn new() -> Self {
Self {
indent_level: 0,
indent_size: 4,
}
}
/// 生成RustScript代码
pub fn generate(&self, document: MATLABDocument) -> Result<String> {
let mut output = String::new();
for node in document.nodes {
let line = self.generate_node(&node)?;
if !line.is_empty() {
output.push_str(&line);
output.push('\n');
}
}
Ok(output)
}
/// 生成单个节点的代码
fn generate_node(&self, node: &MATLABNode) -> Result<String> {
match node {
MATLABNode::Comment(comment) => {
// 将 % 注释转换为 // 注释
Ok(format!("//{}", comment))
}
MATLABNode::FunctionDef { name, output_var } => {
// 函数定义转换为 fn(可选)
Ok(format!("fn {}() -> {} {{", name, output_var))
}
MATLABNode::Assignment { target, value } => {
let value_str = self.generate_value(value)?;
Ok(format!("{} = {};", target, value_str))
}
MATLABNode::BlankLine => {
Ok(String::new())
}
MATLABNode::SectionHeader(header) => {
// 章节头转换为注释
Ok(format!("// {}", header))
}
}
}
/// 生成值的代码
fn generate_value(&self, value: &MATLABValue) -> Result<String> {
match value {
MATLABValue::Scalar(n) => {
Ok(self.format_number(*n))
}
MATLABValue::String(s) => {
Ok(format!("\"{}\"", s))
}
MATLABValue::Matrix(rows) => {
self.generate_aligned_matrix(rows)
}
MATLABValue::CellArray(elements) => {
let mut result = String::from("[\n");
for (i, element) in elements.iter().enumerate() {
result.push_str(&self.get_indent());
result.push('\t');
result.push_str(&self.generate_value(element)?);
if i < elements.len() - 1 {
result.push(',');
}
result.push('\n');
}
result.push_str(&self.get_indent());
result.push(']');
Ok(result)
}
MATLABValue::StructField { object: _, field: _, value } => {
// 结构体字段已经在转换阶段处理
self.generate_value(value)
}
}
}
/// 生成对齐的矩阵代码
fn generate_aligned_matrix(&self, rows: &[Vec<MATLABValue>]) -> Result<String> {
if rows.is_empty() {
return Ok("[]".to_string());
}
// 计算每列的最大宽度
let column_widths = self.calculate_column_widths(rows)?;
let mut result = String::from("[\n");
for (i, row) in rows.iter().enumerate() {
result.push_str(&self.get_indent());
result.push('\t');
result.push('[');
// 格式化每个元素并对齐
for (j, element) in row.iter().enumerate() {
if j > 0 {
result.push_str(", ");
}
let formatted_element = self.format_matrix_element(element)?;
let width = column_widths.get(j).unwrap_or(&0);
// 右对齐数字
result.push_str(&format!("{:>width$}", formatted_element, width = width));
}
result.push(']');
if i < rows.len() - 1 {
result.push(',');
}
result.push('\n');
}
result.push_str(&self.get_indent());
result.push(']');
Ok(result)
}
/// 计算每列的最大宽度
fn calculate_column_widths(&self, rows: &[Vec<MATLABValue>]) -> Result<Vec<usize>> {
if rows.is_empty() {
return Ok(Vec::new());
}
let num_columns = rows[0].len();
let mut column_widths = vec![0; num_columns];
for row in rows {
for (j, element) in row.iter().enumerate() {
if j < num_columns {
let formatted = self.format_matrix_element(element)?;
column_widths[j] = column_widths[j].max(formatted.len());
}
}
}
Ok(column_widths)
}
/// 格式化矩阵元素
fn format_matrix_element(&self, element: &MATLABValue) -> Result<String> {
match element {
MATLABValue::Scalar(n) => Ok(self.format_number(*n)),
MATLABValue::String(s) => Ok(format!("\"{}\"", s)),
_ => Ok("?".to_string()), // 其他类型暂不支持
}
}
/// 格式化数字,保持合理的精度
fn format_number(&self, n: f64) -> String {
if n.fract() == 0.0 && n.abs() < 1e10 {
// 整数
format!("{}", n as i64)
} else if n.abs() < 1e-10 {
// 接近零的数
"0".to_string()
} else if n.abs() < 1e-3 || n.abs() >= 1e6 {
// 科学计数法
format!("{:.6e}", n)
} else {
// 普通小数,自动选择合适的精度
let formatted = format!("{:.10}", n);
// 移除尾部的零
let trimmed = formatted.trim_end_matches('0').trim_end_matches('.');
if trimmed.is_empty() {
"0".to_string()
} else {
trimmed.to_string()
}
}
}
/// 获取当前缩进
fn get_indent(&self) -> String {
" ".repeat(self.indent_level * self.indent_size)
}
}
impl Default for CodeGenerator {
fn default() -> Self {
Self::new()
}
}
use crate::ast::{MATLABDocument, MATLABNode, MATLABValue};
use std::io::Result;
/// 转换器,负责将MATLAB AST转换为RustScript AST
pub struct Converter {
// 可以在这里添加转换选项和配置
}
impl Converter {
pub fn new() -> Self {
Self {}
}
/// 转换整个MATLAB文档
pub fn convert(&self, document: MATLABDocument) -> Result<MATLABDocument> {
let mut converted_document = MATLABDocument::new();
for node in document.nodes {
if let Some(converted_node) = self.convert_node(node)? {
converted_document.add_node(converted_node);
}
}
Ok(converted_document)
}
/// 转换单个节点
fn convert_node(&self, node: MATLABNode) -> Result<Option<MATLABNode>> {
match node {
MATLABNode::Comment(comment) => {
// 将 % 注释转换为 // 注释
Ok(Some(MATLABNode::Comment(comment)))
}
MATLABNode::FunctionDef { name: _, output_var: _ } => {
// 函数定义暂时跳过(根据需求决定是否转换)
Ok(None)
}
MATLABNode::Assignment { target, value } => {
let converted_value = self.convert_value(value)?;
let converted_target = self.convert_target(target)?;
Ok(Some(MATLABNode::Assignment {
target: converted_target,
value: converted_value,
}))
}
MATLABNode::BlankLine => {
Ok(Some(MATLABNode::BlankLine))
}
MATLABNode::SectionHeader(header) => {
Ok(Some(MATLABNode::SectionHeader(header)))
}
}
}
/// 转换赋值目标(移除结构体前缀)
fn convert_target(&self, target: String) -> Result<String> {
// 移除 mpc. 前缀
if target.starts_with("mpc.") {
Ok(target[4..].to_string())
} else {
Ok(target)
}
}
/// 转换值
fn convert_value(&self, value: MATLABValue) -> Result<MATLABValue> {
match value {
MATLABValue::Scalar(n) => Ok(MATLABValue::Scalar(n)),
MATLABValue::String(s) => Ok(MATLABValue::String(s)),
MATLABValue::Matrix(rows) => {
let mut converted_rows = Vec::new();
for row in rows {
let mut converted_row = Vec::new();
for element in row {
converted_row.push(self.convert_value(element)?);
}
converted_rows.push(converted_row);
}
Ok(MATLABValue::Matrix(converted_rows))
}
MATLABValue::CellArray(elements) => {
let mut converted_elements = Vec::new();
for element in elements {
converted_elements.push(self.convert_value(element)?);
}
Ok(MATLABValue::CellArray(converted_elements))
}
MATLABValue::StructField { object: _, field: _, value } => {
// 如果是结构体字段赋值,转换为直接赋值
let converted_value = self.convert_value(*value)?;
Ok(converted_value)
}
}
}
}
impl Default for Converter {
fn default() -> Self {
Self::new()
}
}
use std::io::{Result, Error, ErrorKind};
/// 词法分析器的Token类型
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
// 关键字
Function,
End,
// 标识符和字面量
Identifier(String),
Number(f64),
String(String),
// 运算符和分隔符
Assign, // =
Semicolon, // ;
Comma, // ,
LeftParen, // (
RightParen, // )
LeftBracket, // [
RightBracket, // ]
LeftBrace, // {
RightBrace, // }
Dot, // .
// 注释和空白
Comment(String),
Newline,
// 文件结束
EOF,
}
/// 词法分析器
pub struct Lexer {
input: String,
position: usize,
current_char: Option<char>,
line: usize,
column: usize,
}
impl Lexer {
pub fn new() -> Self {
Self {
input: String::new(),
position: 0,
current_char: None,
line: 1,
column: 1,
}
}
/// 对输入字符串进行词法分析
pub fn tokenize(&mut self, input: &str) -> Result<Vec<Token>> {
self.input = input.to_string();
self.position = 0;
self.line = 1;
self.column = 1;
self.current_char = self.input.chars().next();
let mut tokens = Vec::new();
while let Some(ch) = self.current_char {
match ch {
' ' | '\t' => {
self.advance();
}
'\n' => {
tokens.push(Token::Newline);
self.advance();
self.line += 1;
self.column = 1;
}
'\r' => {
self.advance();
if self.current_char == Some('\n') {
tokens.push(Token::Newline);
self.advance();
self.line += 1;
self.column = 1;
}
}
'%' => {
tokens.push(self.read_comment()?);
}
'=' => {
tokens.push(Token::Assign);
self.advance();
}
';' => {
tokens.push(Token::Semicolon);
self.advance();
}
',' => {
tokens.push(Token::Comma);
self.advance();
}
'(' => {
tokens.push(Token::LeftParen);
self.advance();
}
')' => {
tokens.push(Token::RightParen);
self.advance();
}
'[' => {
tokens.push(Token::LeftBracket);
self.advance();
}
']' => {
tokens.push(Token::RightBracket);
self.advance();
}
'{' => {
tokens.push(Token::LeftBrace);
self.advance();
}
'}' => {
tokens.push(Token::RightBrace);
self.advance();
}
'.' => {
tokens.push(Token::Dot);
self.advance();
}
'\'' => {
tokens.push(self.read_string()?);
}
c if c.is_alphabetic() || c == '_' => {
tokens.push(self.read_identifier()?);
}
c if c.is_ascii_digit() || c == '-' => {
tokens.push(self.read_number()?);
}
_ => {
self.advance();
}
}
}
tokens.push(Token::EOF);
Ok(tokens)
}
fn advance(&mut self) {
self.position += 1;
self.column += 1;
if self.position >= self.input.len() {
self.current_char = None;
} else {
self.current_char = self.input.chars().nth(self.position);
}
}
fn read_comment(&mut self) -> Result<Token> {
let mut comment = String::new();
// 跳过 %
self.advance();
// 读取到行尾
while let Some(ch) = self.current_char {
if ch == '\n' || ch == '\r' {
break;
}
comment.push(ch);
self.advance();
}
Ok(Token::Comment(comment))
}
fn read_string(&mut self) -> Result<Token> {
let mut string_value = String::new();
// 跳过开始的单引号
self.advance();
while let Some(ch) = self.current_char {
if ch == '\'' {
self.advance();
break;
}
string_value.push(ch);
self.advance();
}
Ok(Token::String(string_value))
}
fn read_identifier(&mut self) -> Result<Token> {
let mut identifier = String::new();
while let Some(ch) = self.current_char {
if ch.is_alphanumeric() || ch == '_' {
identifier.push(ch);
self.advance();
} else {
break;
}
}
// 检查是否是关键字
let token = match identifier.as_str() {
"function" => Token::Function,
"end" => Token::End,
_ => Token::Identifier(identifier),
};
Ok(token)
}
fn read_number(&mut self) -> Result<Token> {
let mut number_str = String::new();
// 处理负号
if self.current_char == Some('-') {
number_str.push('-');
self.advance();
}
// 读取数字部分
while let Some(ch) = self.current_char {
if ch.is_ascii_digit() || ch == '.' {
number_str.push(ch);
self.advance();
} else {
break;
}
}
let number = number_str.parse::<f64>().map_err(|_| {
Error::new(ErrorKind::InvalidData, format!("Invalid number: {}", number_str))
})?;
Ok(Token::Number(number))
}
}
impl Default for Lexer {
fn default() -> Self {
Self::new()
}
}
pub mod ast;
pub mod lexer;
pub mod parser;
pub mod converter;
pub mod codegen;
use std::fs;
use std::io::Result;
pub use ast::{MATLABDocument, MATLABNode, MATLABValue};
pub use lexer::Lexer;
pub use parser::Parser;
pub use converter::Converter;
pub use codegen::CodeGenerator;
/// 主要的转换器,负责协调整个转换过程
pub struct MATLABToRustConverter {
// 移除字段,改为在方法中创建实例
}
impl MATLABToRustConverter {
pub fn new() -> Self {
Self {}
}
/// 转换MATLAB文件到RustScript文件
pub fn convert_file(&self, input_path: &str, output_path: &str) -> Result<()> {
// 1. 读取MATLAB文件
let matlab_content = fs::read_to_string(input_path)?;
// 2. 转换内容
let rustscript_content = self.convert_string(&matlab_content)?;
// 3. 写入输出文件
fs::write(output_path, rustscript_content)?;
Ok(())
}
/// 转换MATLAB字符串到RustScript字符串
pub fn convert_string(&self, matlab_content: &str) -> Result<String> {
// 1. 词法分析
let mut lexer = Lexer::new();
let tokens = lexer.tokenize(matlab_content)?;
// 2. 语法分析,构建AST
let mut parser = Parser::new();
let ast = parser.parse(tokens)?;
// 3. 转换AST
let converter = Converter::new();
// let converted_ast = converter.convert(ast)?;
let converted_ast = ast;
// 4. 生成RustScript代码
let codegen = CodeGenerator::new();
let rustscript_code = codegen.generate(converted_ast)?;
Ok(rustscript_code)
}
}
impl Default for MATLABToRustConverter {
fn default() -> Self {
Self::new()
}
}
use crate::ast::{MATLABDocument, MATLABNode, MATLABValue};
use crate::lexer::Token;
use std::io::{Result, Error, ErrorKind};
/// 语法分析器
pub struct Parser {
tokens: Vec<Token>,
current: usize,
}
impl Parser {
pub fn new() -> Self {
Self {
tokens: Vec::new(),
current: 0,
}
}
/// 解析tokens并构建AST
pub fn parse(&mut self, tokens: Vec<Token>) -> Result<MATLABDocument> {
self.tokens = tokens;
self.current = 0;
let mut document = MATLABDocument::new();
while !self.is_at_end() {
if let Some(node) = self.parse_node()? {
document.add_node(node);
}
}
Ok(document)
}
fn parse_node(&mut self) -> Result<Option<MATLABNode>> {
match self.current_token() {
Some(Token::Comment(comment)) => {
let node = MATLABNode::Comment(comment.clone());
self.advance();
Ok(Some(node))
}
Some(Token::Function) => {
Ok(Some(self.parse_function_def()?))
}
Some(Token::Identifier(name)) => {
let name = name.clone();
self.advance();
// 检查是否是赋值语句
if self.match_token(&Token::Assign) {
let value = self.parse_value()?;
Ok(Some(MATLABNode::Assignment { target: name, value }))
} else {
// 如果不是赋值,可能是其他语句,暂时跳过
Ok(None)
}
}
Some(Token::Newline) => {
self.advance();
Ok(Some(MATLABNode::BlankLine))
}
_ => {
self.advance();
Ok(None)
}
}
}
fn parse_function_def(&mut self) -> Result<MATLABNode> {
// 跳过 'function'
self.advance();
// 读取输出变量
let output_var = if let Some(Token::Identifier(name)) = self.current_token() {
let name = name.clone();
self.advance();
name
} else {
return Err(Error::new(ErrorKind::InvalidData, "Expected output variable after 'function'"));
};
// 跳过 '='
if !self.match_token(&Token::Assign) {
return Err(Error::new(ErrorKind::InvalidData, "Expected '=' after output variable"));
}
// 读取函数名
let function_name = if let Some(Token::Identifier(name)) = self.current_token() {
let name = name.clone();
self.advance();
name
} else {
return Err(Error::new(ErrorKind::InvalidData, "Expected function name"));
};
Ok(MATLABNode::FunctionDef {
name: function_name,
output_var,
})
}
fn parse_value(&mut self) -> Result<MATLABValue> {
match self.current_token() {
Some(Token::Number(n)) => {
let value = *n;
self.advance();
Ok(MATLABValue::Scalar(value))
}
Some(Token::String(s)) => {
let value = s.clone();
self.advance();
Ok(MATLABValue::String(value))
}
Some(Token::LeftBracket) => {
self.parse_matrix()
}
Some(Token::LeftBrace) => {
self.parse_cell_array()
}
Some(Token::Identifier(name)) => {
let name = name.clone();
self.advance();
// 检查是否是结构体字段访问
if self.match_token(&Token::Dot) {
if let Some(Token::Identifier(field)) = self.current_token() {
let field = field.clone();
self.advance();
if self.match_token(&Token::Assign) {
let value = self.parse_value()?;
Ok(MATLABValue::StructField {
object: name,
field,
value: Box::new(value),
})
} else {
Err(Error::new(ErrorKind::InvalidData, "Expected assignment after struct field"))
}
} else {
Err(Error::new(ErrorKind::InvalidData, "Expected field name after '.'"))
}
} else {
// 普通标识符,暂时作为字符串处理
Ok(MATLABValue::String(name))
}
}
_ => {
Err(Error::new(ErrorKind::InvalidData, "Unexpected token in value"))
}
}
}
fn parse_matrix(&mut self) -> Result<MATLABValue> {
// 跳过 '['
self.advance();
let mut rows = Vec::new();
let mut current_row = Vec::new();
while !self.check(&Token::RightBracket) && !self.is_at_end() {
match self.current_token() {
Some(Token::Number(n)) => {
current_row.push(MATLABValue::Scalar(*n));
self.advance();
}
Some(Token::String(s)) => {
current_row.push(MATLABValue::String(s.clone()));
self.advance();
}
Some(Token::Semicolon) => {
if !current_row.is_empty() {
rows.push(current_row);
current_row = Vec::new();
}
self.advance();
}
Some(Token::Newline) => {
self.advance();
}
_ => {
self.advance();
}
}
}
// 添加最后一行
if !current_row.is_empty() {
rows.push(current_row);
}
// 跳过 ']'
if self.match_token(&Token::RightBracket) {
Ok(MATLABValue::Matrix(rows))
} else {
Err(Error::new(ErrorKind::InvalidData, "Expected ']' to close matrix"))
}
}
fn parse_cell_array(&mut self) -> Result<MATLABValue> {
// 跳过 '{'
self.advance();
let mut elements = Vec::new();
while !self.check(&Token::RightBrace) && !self.is_at_end() {
match self.current_token() {
Some(Token::String(s)) => {
elements.push(MATLABValue::String(s.clone()));
self.advance();
}
Some(Token::Semicolon) | Some(Token::Newline) => {
self.advance();
}
_ => {
self.advance();
}
}
}
// 跳过 '}'
if self.match_token(&Token::RightBrace) {
Ok(MATLABValue::CellArray(elements))
} else {
Err(Error::new(ErrorKind::InvalidData, "Expected '}' to close cell array"))
}
}
fn current_token(&self) -> Option<&Token> {
self.tokens.get(self.current)
}
fn advance(&mut self) {
if !self.is_at_end() {
self.current += 1;
}
}
fn is_at_end(&self) -> bool {
self.current >= self.tokens.len() ||
matches!(self.current_token(), Some(Token::EOF))
}
fn check(&self, token: &Token) -> bool {
if let Some(current) = self.current_token() {
std::mem::discriminant(current) == std::mem::discriminant(token)
} else {
false
}
}
fn match_token(&mut self, token: &Token) -> bool {
if self.check(token) {
self.advance();
true
} else {
false
}
}
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}
use rustscript_transpiler::MATLABToRustConverter;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_conversion() {
let matlab_code = r#"
function mpc = case14
%CASE14 Power flow data for IEEE 14 bus test case.
% This is a test comment
%% system MVA base
mpc.baseMVA = 100;
%% bus data
mpc.bus = [
1 3 0 0 0 0 1 1.06 0 0 1 1.06 0.94;
2 2 21.7 12.7 0 0 1 1.045 -4.98 0 1 1.06 0.94;
];
%% generator cost data
mpc.gencost = [
2 0 0 3 0.0430292599 20 0;
2 0 0 3 0.25 20 0;
];
"#;
let converter = MATLABToRustConverter::new();
let result = converter.convert_string(matlab_code);
match result {
Ok(rustscript_code) => {
println!("转换结果:\n{}", rustscript_code);
// 验证转换是否保持了原始顺序
assert!(rustscript_code.contains("//CASE14"));
assert!(rustscript_code.contains("baseMVA = 100;"));
assert!(rustscript_code.contains("bus = ["));
assert!(rustscript_code.contains("gencost = ["));
// 验证矩阵格式是否正确
assert!(rustscript_code.contains("[1, 3, 0, 0, 0, 0, 1, 1.06, 0, 0, 1, 1.06, 0.94]"));
}
Err(e) => {
panic!("转换失败: {}", e);
}
}
}
#[test]
fn test_order_preservation() {
let matlab_code = r#"
% First comment
mpc.first = 1;
% Second comment
mpc.second = 2;
% Third comment
mpc.third = 3;
"#;
let converter = MATLABToRustConverter::new();
let result = converter.convert_string(matlab_code).unwrap();
// 验证顺序是否保持
let lines: Vec<&str> = result.lines().collect();
assert!(lines.iter().position(|&line| line.contains("First comment")).unwrap() <
lines.iter().position(|&line| line.contains("first = 1")).unwrap());
assert!(lines.iter().position(|&line| line.contains("first = 1")).unwrap() <
lines.iter().position(|&line| line.contains("Second comment")).unwrap());
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论