Commit 3802e0a9 by wzc-a

新建cargo: matlab自动解析为rustscript

parent bd680aed
......@@ -1732,6 +1732,15 @@ dependencies = [
]
[[package]]
name = "rustscript-transpiler"
version = "0.1.0"
dependencies = [
"anyhow",
"regex",
"serde",
]
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
......
......@@ -13,6 +13,7 @@ members = [
"mems/examples/ds-powerflow/ds-3phase-pf",
"mems/examples/ds-guizhou",
"mems/examples/iesplan",
"rustscript-transpiler",
]
[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]
# 用于测试的依赖
# 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论