重构4
This commit is contained in:
@@ -1,434 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
分析 TLUSTY Fortran 文件,提取函数依赖信息。
|
||||
|
||||
用法:
|
||||
python3 analyze_fortran.py # 输出 CSV(带完整依赖)
|
||||
python3 analyze_fortran.py --tree # 输出依赖树(文本格式)
|
||||
python3 analyze_fortran.py --priority # 输出重构优先级列表
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
|
||||
def extract_includes(content):
|
||||
"""提取 INCLUDE 文件列表"""
|
||||
includes = re.findall(r"INCLUDE\s*'([^']+)\.FOR'", content, re.IGNORECASE)
|
||||
return [inc for inc in includes if inc.upper() != 'IMPLIC']
|
||||
|
||||
def extract_commons(content):
|
||||
"""提取 COMMON 块名称"""
|
||||
# 匹配 COMMON/NAME/ 或 common/name/
|
||||
commons = re.findall(r'(?i)^\s*COMMON\s*/(\w+)/', content, re.MULTILINE)
|
||||
return list(set(commons))
|
||||
|
||||
# Fortran 内置函数列表(不需要追踪)
|
||||
FORTRAN_INTRINSICS = {
|
||||
'SIN', 'COS', 'TAN', 'ASIN', 'ACOS', 'ATAN', 'ATAN2',
|
||||
'SINH', 'COSH', 'TANH',
|
||||
'EXP', 'LOG', 'LOG10', 'LOG2',
|
||||
'SQRT', 'ABS', 'MOD', 'SIGN',
|
||||
'MAX', 'MIN', 'MAX0', 'MIN0', 'MAX1', 'MIN1', 'AMAX0', 'AMIN0',
|
||||
'INT', 'IFIX', 'IDINT', 'FLOAT', 'SNGL', 'DBLE', 'CMPLX',
|
||||
'REAL', 'AIMAG', 'CONJG',
|
||||
'ICHAR', 'CHAR', 'INDEX', 'LEN', 'LGE', 'LGT', 'LLE', 'LLT',
|
||||
'DOT_PRODUCT', 'MATMUL', 'TRANSPOSE', 'RESHAPE',
|
||||
'SIZE', 'SHAPE', 'LBOUND', 'UBOUND',
|
||||
'ALLOCATED', 'ALLOCATE', 'DEALLOCATE',
|
||||
'KIND', 'SELECTED_REAL_KIND', 'SELECTED_INT_KIND',
|
||||
'DIGITS', 'EPSILON', 'HUGE', 'TINY', 'PRECISION', 'RANGE',
|
||||
'FLOOR', 'CEILING', 'NINT', 'ANINT',
|
||||
'ADJUSTL', 'ADJUSTR', 'TRIM', 'REPEAT', 'SCAN', 'VERIFY',
|
||||
'PRESENT', 'ASSOCIATED',
|
||||
# TLUSTY 常用数学函数
|
||||
'ERF', 'ERFC', 'GAMMA', 'LOG_GAMMA',
|
||||
}
|
||||
|
||||
def extract_calls(content, known_functions=None):
|
||||
"""提取 CALL 语句和 FUNCTION 调用
|
||||
|
||||
Args:
|
||||
content: Fortran 源码
|
||||
known_functions: 已知的函数名集合(用于区分函数调用和数组访问)
|
||||
"""
|
||||
calls = set()
|
||||
|
||||
# 1. 提取 CALL 语句(支持有括号和无括号两种形式)
|
||||
# CALL NAME(...) 或 CALL NAME
|
||||
call_stmts = re.findall(r'(?i)CALL\s+(\w+)(?:\s*\(|\s*$|\s*\n)', content)
|
||||
calls.update(c.upper() for c in call_stmts)
|
||||
|
||||
# 2. 提取可能的 FUNCTION 调用
|
||||
if known_functions:
|
||||
# 只匹配已知函数名
|
||||
func_assign = re.findall(r'(?i)=\s*([A-Z][A-Z0-9]*)\s*\(', content)
|
||||
calls.update(f.upper() for f in func_assign
|
||||
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
|
||||
|
||||
func_expr = re.findall(r'(?i)[=(,]\s*([A-Z][A-Z0-9]*)\s*\(', content)
|
||||
calls.update(f.upper() for f in func_expr
|
||||
if f.upper() in known_functions and f.upper() not in FORTRAN_INTRINSICS)
|
||||
|
||||
return list(calls)
|
||||
|
||||
def has_file_io(content):
|
||||
"""检查是否有文件 I/O"""
|
||||
patterns = [
|
||||
r'OPEN\s*\(',
|
||||
r'READ\s*\(\s*\d+',
|
||||
r'WRITE\s*\(\s*\d+',
|
||||
r'write\s*\(',
|
||||
r'read\s*\(',
|
||||
]
|
||||
for p in patterns:
|
||||
if re.search(p, content, re.IGNORECASE):
|
||||
return True
|
||||
return False
|
||||
|
||||
def extract_unit_info(content, filename):
|
||||
"""提取单元信息"""
|
||||
units = []
|
||||
|
||||
# 匹配 SUBROUTINE
|
||||
sub_match = re.search(r'(?i)^\s*SUBROUTINE\s+(\w+)', content, re.MULTILINE)
|
||||
if sub_match:
|
||||
units.append(('SUBROUTINE', sub_match.group(1).upper()))
|
||||
|
||||
# 匹配 FUNCTION
|
||||
func_match = re.search(r'(?i)^\s*(?:REAL(?:\*\d+)?|INTEGER(?:\*\d+)?|DOUBLE\s*PRECISION)?\s*FUNCTION\s+(\w+)', content, re.MULTILINE)
|
||||
if func_match:
|
||||
units.append(('FUNCTION', func_match.group(1).upper()))
|
||||
|
||||
# 匹配 BLOCK DATA
|
||||
block_match = re.search(r'(?i)^\s*BLOCK\s*DATA\s+(\w+)?', content, re.MULTILINE)
|
||||
if block_match:
|
||||
name = block_match.group(1).upper() if block_match.group(1) else '_UNNAMED_'
|
||||
units.append(('BLOCK DATA', name))
|
||||
|
||||
# 如果都没匹配到,使用文件名
|
||||
if not units:
|
||||
base = os.path.splitext(filename)[0]
|
||||
units.append(('UNKNOWN', base.upper()))
|
||||
|
||||
return units
|
||||
|
||||
# 特殊映射:一个 Rust 文件实现多个 Fortran 函数
|
||||
SPECIAL_MAPPINGS = {
|
||||
# Rust 文件名 -> [Fortran 函数名列表]
|
||||
'gfree': ['gfree0', 'gfreed', 'gfree1'],
|
||||
'interpolate': ['yint', 'lagran'],
|
||||
'sgmer': ['sgmer0', 'sgmer1', 'sgmerd'],
|
||||
'ctdata': ['hction', 'hctrecom'],
|
||||
'cross': ['cross', 'crossd'],
|
||||
'expint': ['eint', 'expinx'],
|
||||
'erfcx': ['erfcx', 'erfcin'],
|
||||
'lineqs': ['lineqs', 'lineqs_nr'],
|
||||
'gamsp': ['gamsp'], # alias
|
||||
'bhe': ['bhe', 'bhed', 'bhez'], # 流体静力学平衡方程
|
||||
'comset': ['comset'], # Compton 散射参数设置
|
||||
'ghydop': ['ghydop'], # 氢不透明度 (Gomez 表)
|
||||
'levgrp': ['levgrp'], # 能级分组
|
||||
'profil': ['profil'], # 标准吸收轮廓
|
||||
'linspl': ['linspl'], # 谱线轮廓设置
|
||||
}
|
||||
|
||||
def find_rust_module(fortran_name, rust_dir):
|
||||
"""查找对应的 Rust 模块"""
|
||||
# 先检查直接匹配
|
||||
rust_file = os.path.join(rust_dir, f"{fortran_name}.rs")
|
||||
if os.path.exists(rust_file):
|
||||
return f"src/math/{fortran_name}.rs"
|
||||
|
||||
# 检查特殊映射
|
||||
for rust_mod, fortran_funcs in SPECIAL_MAPPINGS.items():
|
||||
if fortran_name in fortran_funcs:
|
||||
return f"src/math/{rust_mod}.rs"
|
||||
|
||||
return ""
|
||||
|
||||
def get_transitive_deps(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递调用依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
all_deps = set(direct_calls)
|
||||
|
||||
for dep in direct_calls:
|
||||
all_deps.update(get_transitive_deps(dep, units_dict, visited.copy()))
|
||||
|
||||
return all_deps
|
||||
|
||||
def get_pending_deps(unit_name, units_dict, visited=None):
|
||||
"""获取尚未实现的直接依赖"""
|
||||
if unit_name not in units_dict:
|
||||
return []
|
||||
|
||||
calls = units_dict[unit_name].get('call_deps', [])
|
||||
pending = [d for d in calls if d not in units_dict or units_dict[d].get('status') != 'done']
|
||||
return pending
|
||||
|
||||
def get_transitive_pending_deps(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递的未实现依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
# 未实现的直接依赖
|
||||
pending_deps = set(d for d in direct_calls if d not in units_dict or units_dict[d].get('status') != 'done')
|
||||
|
||||
# 递归获取所有依赖的未实现依赖
|
||||
for dep in direct_calls:
|
||||
pending_deps.update(get_transitive_pending_deps(dep, units_dict, visited.copy()))
|
||||
|
||||
return pending_deps
|
||||
|
||||
def get_transitive_commons(unit_name, units_dict, visited=None):
|
||||
"""递归获取所有传递 COMMON 依赖"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
return set()
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return set()
|
||||
|
||||
direct_commons = set(units_dict[unit_name].get('common_deps', []))
|
||||
direct_calls = units_dict[unit_name].get('call_deps', [])
|
||||
|
||||
all_commons = direct_commons.copy()
|
||||
|
||||
for dep in direct_calls:
|
||||
all_commons.update(get_transitive_commons(dep, units_dict, visited.copy()))
|
||||
|
||||
return all_commons
|
||||
|
||||
def calculate_depth(unit_name, units_dict, memo=None):
|
||||
"""计算依赖深度(叶子节点深度为0)"""
|
||||
if memo is None:
|
||||
memo = {}
|
||||
|
||||
if unit_name in memo:
|
||||
return memo[unit_name]
|
||||
|
||||
if unit_name not in units_dict:
|
||||
return 0
|
||||
|
||||
calls = units_dict[unit_name].get('call_deps', [])
|
||||
if not calls:
|
||||
memo[unit_name] = 0
|
||||
return 0
|
||||
|
||||
max_dep_depth = 0
|
||||
for dep in calls:
|
||||
if dep != unit_name: # 避免自引用
|
||||
max_dep_depth = max(max_dep_depth, calculate_depth(dep, units_dict, memo))
|
||||
|
||||
depth = max_dep_depth + 1
|
||||
memo[unit_name] = depth
|
||||
return depth
|
||||
|
||||
def print_dependency_tree(unit_name, units_dict, indent=0, visited=None, prefix="", show_pending_count=True):
|
||||
"""打印依赖树(文本格式)"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if unit_name in visited:
|
||||
print(f"{prefix}[循环引用: {unit_name}]")
|
||||
return
|
||||
|
||||
visited.add(unit_name)
|
||||
|
||||
if unit_name not in units_dict:
|
||||
print(f"{prefix}{unit_name} [未找到/未实现]")
|
||||
return
|
||||
|
||||
unit = units_dict[unit_name]
|
||||
status = unit.get('status', 'pending')
|
||||
status_mark = "✓" if status == "done" else "○"
|
||||
|
||||
# 计算未实现依赖数
|
||||
pending_count = len(get_pending_deps(unit_name, units_dict))
|
||||
pending_str = f" ({pending_count}未实现)" if show_pending_count and pending_count > 0 else ""
|
||||
|
||||
print(f"{prefix}{status_mark} {unit_name}{pending_str}")
|
||||
|
||||
calls = unit.get('call_deps', [])
|
||||
# 按未实现依赖数排序(未实现多的在前,因为更紧迫)
|
||||
pending_sorted = sorted(calls, key=lambda d: -len(get_pending_deps(d, units_dict) if d in units_dict else []))
|
||||
|
||||
for i, dep in enumerate(pending_sorted):
|
||||
is_last = (i == len(pending_sorted) - 1)
|
||||
connector = "└── " if is_last else "├── "
|
||||
print_dependency_tree(dep, units_dict, indent + 1, visited.copy(), prefix + connector, show_pending_count)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='分析 TLUSTY Fortran 文件依赖')
|
||||
parser.add_argument('--tree', metavar='UNIT', help='输出指定单元的依赖树')
|
||||
parser.add_argument('--priority', action='store_true', help='输出重构优先级列表')
|
||||
parser.add_argument('--full', action='store_true', help='输出完整传递依赖')
|
||||
args = parser.parse_args()
|
||||
|
||||
extracted_dir = "/home/fmq/program/tlusty/tl208-s54/rust/tlusty/extracted"
|
||||
rust_dir = "/home/fmq/program/tlusty/tl208-s54/rust/src/math"
|
||||
|
||||
# 第一遍:收集所有已定义的 SUBROUTINE 和 FUNCTION 名称
|
||||
all_defined_units = set()
|
||||
fortran_files = sorted(glob.glob(os.path.join(extracted_dir, "*.f")))
|
||||
|
||||
for fpath in fortran_files:
|
||||
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
units = extract_unit_info(content, os.path.basename(fpath))
|
||||
for unit_type, unit_name in units:
|
||||
all_defined_units.add(unit_name)
|
||||
|
||||
# 第二遍:收集所有单元信息(使用已知函数名来过滤调用)
|
||||
units_dict = {}
|
||||
|
||||
for fpath in fortran_files:
|
||||
fname = os.path.basename(fpath)
|
||||
base_name = os.path.splitext(fname)[0]
|
||||
|
||||
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
content = f.read()
|
||||
|
||||
includes = extract_includes(content)
|
||||
commons = extract_commons(content)
|
||||
calls = extract_calls(content, known_functions=all_defined_units)
|
||||
io = has_file_io(content)
|
||||
units = extract_unit_info(content, fname)
|
||||
|
||||
is_pure = len(includes) <= 1 and len(commons) == 0 and not io
|
||||
rust_mod = find_rust_module(base_name, rust_dir)
|
||||
status = "done" if rust_mod else "pending"
|
||||
|
||||
for unit_type, unit_name in units:
|
||||
units_dict[unit_name] = {
|
||||
'fortran_file': fname,
|
||||
'unit_type': unit_type,
|
||||
'is_pure': is_pure,
|
||||
'common_deps': includes + commons,
|
||||
'call_deps': calls,
|
||||
'has_io': io,
|
||||
'rust_module': rust_mod,
|
||||
'status': status,
|
||||
}
|
||||
|
||||
# --tree 模式:输出依赖树
|
||||
if args.tree:
|
||||
unit_name = args.tree.upper()
|
||||
if unit_name in units_dict:
|
||||
unit = units_dict[unit_name]
|
||||
trans_pending = get_transitive_pending_deps(unit_name, units_dict)
|
||||
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||
status_mark = "✓" if unit['status'] == "done" else "○"
|
||||
|
||||
print(f"依赖树: {unit_name} {status_mark}")
|
||||
print("=" * 60)
|
||||
print(f"直接依赖: {len(unit['call_deps'])}, 传递依赖: {len(trans_calls)}, "
|
||||
f"未实现: {len(trans_pending)}")
|
||||
if trans_pending:
|
||||
print(f"未实现依赖: {', '.join(sorted(trans_pending)[:10])}")
|
||||
if len(trans_pending) > 10:
|
||||
print(f" ... 还有 {len(trans_pending) - 10} 个")
|
||||
print("-" * 60)
|
||||
print_dependency_tree(unit_name, units_dict)
|
||||
else:
|
||||
print(f"未找到单元: {unit_name}")
|
||||
# 尝试模糊匹配
|
||||
matches = [u for u in units_dict if args.tree.lower() in u.lower()]
|
||||
if matches:
|
||||
print(f"可能的匹配: {', '.join(matches[:10])}")
|
||||
return
|
||||
|
||||
# --priority 模式:输出重构优先级
|
||||
if args.priority:
|
||||
# 计算每个单元的依赖深度和传递依赖数
|
||||
priority_list = []
|
||||
memo = {}
|
||||
for unit_name, unit in units_dict.items():
|
||||
if unit['status'] == 'done':
|
||||
continue
|
||||
# 跳过无法识别程序单元的文件(如纯注释文件)
|
||||
if unit['unit_type'] == 'UNKNOWN':
|
||||
continue
|
||||
|
||||
depth = calculate_depth(unit_name, units_dict, memo)
|
||||
trans_calls = len(get_transitive_deps(unit_name, units_dict))
|
||||
trans_commons = len(get_transitive_commons(unit_name, units_dict))
|
||||
pending_deps = len(get_pending_deps(unit_name, units_dict))
|
||||
trans_pending = len(get_transitive_pending_deps(unit_name, units_dict))
|
||||
|
||||
priority_list.append({
|
||||
'name': unit_name,
|
||||
'depth': depth,
|
||||
'direct_calls': len(unit['call_deps']),
|
||||
'trans_calls': trans_calls,
|
||||
'direct_commons': len(unit['common_deps']),
|
||||
'trans_commons': trans_commons,
|
||||
'pending_deps': pending_deps,
|
||||
'trans_pending': trans_pending,
|
||||
'has_io': unit['has_io'],
|
||||
'is_pure': unit['is_pure'],
|
||||
})
|
||||
|
||||
# 按优先级排序:无IO > 未实现依赖少 > 深度低
|
||||
priority_list.sort(key=lambda x: (x['has_io'], x['trans_pending'], x['depth'], x['trans_calls']))
|
||||
|
||||
print("重构优先级列表 (优先无IO,按未实现依赖排序)")
|
||||
print("=" * 100)
|
||||
print(f"{'单元名':<20} {'未实现':>6} {'传递未实现':>10} {'深度':>4} {'直接调用':>8} {'传递调用':>8} {'IO':>4}")
|
||||
print("-" * 100)
|
||||
|
||||
for item in priority_list[:100]: # 显示前100个
|
||||
io_mark = "✓" if item['has_io'] else "○"
|
||||
print(f"{item['name']:<20} {item['pending_deps']:>6} {item['trans_pending']:>10} "
|
||||
f"{item['depth']:>4} {item['direct_calls']:>8} {item['trans_calls']:>8} {io_mark:>4}")
|
||||
return
|
||||
|
||||
# 默认模式:输出 CSV(带完整依赖)
|
||||
if args.full:
|
||||
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,"
|
||||
"trans_commons,trans_calls,has_io,rust_module,status")
|
||||
else:
|
||||
print("fortran_file,unit_name,unit_type,is_pure,common_deps,call_deps,has_io,rust_module,status")
|
||||
|
||||
memo = {}
|
||||
for unit_name, unit in units_dict.items():
|
||||
if args.full:
|
||||
trans_commons = get_transitive_commons(unit_name, units_dict)
|
||||
trans_calls = get_transitive_deps(unit_name, units_dict)
|
||||
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||
f"\"{'|'.join(trans_commons)}\",\"{'|'.join(trans_calls)}\","
|
||||
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||
else:
|
||||
print(f"{unit['fortran_file']},{unit_name},{unit['unit_type']},{unit['is_pure']},"
|
||||
f"\"{'|'.join(unit['common_deps'])}\",\"{'|'.join(unit['call_deps'])}\","
|
||||
f"{unit['has_io']},{unit['rust_module']},{unit['status']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,438 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
从 Fortran 源文件提取数组数据,生成 Rust data.rs
|
||||
|
||||
用法:
|
||||
python3 scripts/extract_fortran_data.py
|
||||
|
||||
输出: src/data.rs
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def parse_fortran_file(filepath: Path, global_params: dict = None) -> list[dict]:
|
||||
"""解析单个 Fortran 文件中的数组
|
||||
|
||||
Args:
|
||||
filepath: Fortran 文件路径
|
||||
global_params: 从 include 文件中提取的全局参数表
|
||||
"""
|
||||
if global_params is None:
|
||||
global_params = {}
|
||||
|
||||
with open(filepath, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
arrays = {}
|
||||
|
||||
# 0. 预处理
|
||||
# 先清理每行的 Fortran 注释 (! 后面的内容)
|
||||
# 同时移除 Fortran 77 风格的注释行 (以 C 或 * 开头)
|
||||
lines = content.split('\n')
|
||||
cleaned_lines = []
|
||||
for line in lines:
|
||||
# Fortran 77 注释行 (第1列是 C, c, *, 或完全空行)
|
||||
if len(line) > 0 and line[0] in 'Cc*':
|
||||
continue # 跳过整行注释
|
||||
# Fortran 90 行内注释
|
||||
if '!' in line:
|
||||
line = line.split('!')[0]
|
||||
cleaned_lines.append(line)
|
||||
|
||||
# 再合并 Fortran 续行 (第6列是 *, +, &, 数字, 或字母)
|
||||
# Fortran 允许使用字母作为续行标记 (A, B, C, ... 用于超过9个续行)
|
||||
merged_lines = []
|
||||
for line in cleaned_lines:
|
||||
# 检查是否是续行 (第6列是 *, +, &, 数字, 或非空格字符)
|
||||
if len(line) >= 6 and line[5] != ' ' and line[5] not in '\n\r\t':
|
||||
# 续行: 追加到上一行 (去掉前6列)
|
||||
if merged_lines:
|
||||
merged_lines[-1] += ' ' + line[6:].strip()
|
||||
else:
|
||||
merged_lines.append(line)
|
||||
content = '\n'.join(merged_lines)
|
||||
|
||||
# 1. 首先解析所有 parameter 语句,建立局部常量表
|
||||
# 合并全局参数和局部参数
|
||||
param_table = dict(global_params) # 复制全局参数
|
||||
param_pattern = r'parameter\s*\(([^)]+)\)'
|
||||
for match in re.finditer(param_pattern, content, re.IGNORECASE):
|
||||
params_str = match.group(1)
|
||||
for param in params_str.split(','):
|
||||
param = param.strip()
|
||||
if '=' in param:
|
||||
name, val = param.split('=', 1)
|
||||
name = name.strip().lower()
|
||||
val = val.strip().lower()
|
||||
# 尝试解析为整数或浮点数
|
||||
try:
|
||||
# 先尝试整数
|
||||
if '.' not in val and 'e' not in val and 'd' not in val:
|
||||
param_table[name] = int(val)
|
||||
else:
|
||||
# 浮点数,转换为整数(用于数组维度)
|
||||
val = val.replace('d', 'e')
|
||||
param_table[name] = int(float(val))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 2. 解析 dimension 语句
|
||||
# dimension p4a(22), p4b(10,28), adi(nni), ...
|
||||
# 注意: 使用 [ \t] 代替 \s 避免跨行匹配
|
||||
dim_pattern = r'dimension[ \t]+([a-z0-9_,() \t]+)'
|
||||
for match in re.finditer(dim_pattern, content, re.IGNORECASE):
|
||||
dim_str = match.group(1)
|
||||
# 清理 dim_str - 移除可能包含的下一个关键字
|
||||
for keyword in ['\nreal', '\ninteger', '\ncomplex', '\nlogical', '\ncharacter',
|
||||
'\ndimension', '\ndata', '\nparameter', '\nequivalence']:
|
||||
if keyword in dim_str.lower():
|
||||
dim_str = dim_str[:dim_str.lower().find(keyword)]
|
||||
break
|
||||
arr_pattern = r'(\w+)\s*\(([^)]+)\)'
|
||||
for arr_match in re.finditer(arr_pattern, dim_str):
|
||||
name = arr_match.group(1).lower()
|
||||
dims_str = arr_match.group(2)
|
||||
# 解析维度,支持常量和 parameter 变量
|
||||
dims = []
|
||||
valid = True
|
||||
for d in dims_str.split(','):
|
||||
d = d.strip().lower()
|
||||
if d in param_table:
|
||||
dims.append(param_table[d])
|
||||
else:
|
||||
try:
|
||||
dims.append(int(d))
|
||||
except ValueError:
|
||||
valid = False
|
||||
break
|
||||
if valid and dims:
|
||||
arrays[name] = {"name": name, "dims": dims, "data": None, "source": filepath.name}
|
||||
|
||||
# 2.5 解析类型声明中的数组
|
||||
# REAL frac(MR), INTEGER arr(10), REAL*4 arr(10), CHARACTER*10 str(5), etc.
|
||||
# 注意: 使用 [ \t] 代替 \s 避免跨行匹配
|
||||
type_decl_pattern = r'(real(?:\*[\d]+)?|integer(?:\*[\d]+)?|complex(?:\*[\d]+)?|logical(?:\*[\d]+)?|character(?:\*[\d]+)?)[ \t]+([a-z0-9_,() \t]+)'
|
||||
for match in re.finditer(type_decl_pattern, content, re.IGNORECASE):
|
||||
decl_str = match.group(2)
|
||||
# 清理 decl_str - 移除可能包含的下一个类型声明
|
||||
for keyword in ['\nreal', '\ninteger', '\ncomplex', '\nlogical', '\ncharacter',
|
||||
'\ndimension', '\ndata', '\nparameter', '\nequivalence']:
|
||||
if keyword in decl_str.lower():
|
||||
decl_str = decl_str[:decl_str.lower().find(keyword)]
|
||||
break
|
||||
|
||||
# 匹配变量名(维度)
|
||||
arr_pattern = r'(\w+)\s*\(([^)]+)\)'
|
||||
for arr_match in re.finditer(arr_pattern, decl_str):
|
||||
name = arr_match.group(1).lower()
|
||||
if name in arrays:
|
||||
continue # 已有定义
|
||||
dims_str = arr_match.group(2)
|
||||
# 解析维度
|
||||
dims = []
|
||||
valid = True
|
||||
for d in dims_str.split(','):
|
||||
d = d.strip().lower()
|
||||
if d in param_table:
|
||||
dims.append(param_table[d])
|
||||
else:
|
||||
try:
|
||||
dims.append(int(d))
|
||||
except ValueError:
|
||||
valid = False
|
||||
break
|
||||
if valid and dims:
|
||||
arrays[name] = {"name": name, "dims": dims, "data": None, "source": filepath.name}
|
||||
|
||||
# 3. 解析 data 语句 (支持多行)
|
||||
# data name / val1, val2, ... /
|
||||
data_pattern = r'data\s+(\w+)\s*/\s*([^/]+)\s*/'
|
||||
for match in re.finditer(data_pattern, content, re.IGNORECASE | re.DOTALL):
|
||||
name = match.group(1).lower()
|
||||
data_str = match.group(2)
|
||||
|
||||
if name not in arrays:
|
||||
arrays[name] = {"name": name, "dims": [], "data": None, "source": filepath.name}
|
||||
|
||||
values = parse_data_values(data_str)
|
||||
arrays[name]["data"] = values
|
||||
|
||||
# 4. 处理 parameter 语句中的标量常量(用于导出)
|
||||
for match in re.finditer(param_pattern, content, re.IGNORECASE):
|
||||
params_str = match.group(1)
|
||||
for param in params_str.split(','):
|
||||
param = param.strip()
|
||||
if '=' in param:
|
||||
name, val = param.split('=', 1)
|
||||
name = name.strip().lower()
|
||||
val = val.strip().lower()
|
||||
if name not in arrays:
|
||||
try:
|
||||
val = val.replace('d', 'e')
|
||||
arrays[name] = {"name": name, "dims": [], "data": [float(val)], "source": filepath.name, "is_param": True}
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return list(arrays.values())
|
||||
|
||||
|
||||
def parse_data_values(data_str: str) -> list[float]:
|
||||
"""解析 DATA 语句中的数值,处理重复语法如 7*1.387"""
|
||||
values = []
|
||||
|
||||
# 清理
|
||||
lines = data_str.split('\n')
|
||||
cleaned_lines = []
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('*'):
|
||||
line = line[1:].strip()
|
||||
cleaned_lines.append(line)
|
||||
data_str = ' '.join(cleaned_lines)
|
||||
|
||||
for part in data_str.split(','):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
|
||||
# 移除末尾的 / (DATA 语句结束符)
|
||||
part = part.rstrip('/')
|
||||
|
||||
# 处理重复语法: "7*1.387"
|
||||
if '*' in part and not part.startswith('-'):
|
||||
match = re.match(r'(\d+)\s*\*\s*(-?[\d.]+)', part)
|
||||
if match:
|
||||
count = int(match.group(1))
|
||||
val = float(match.group(2))
|
||||
values.extend([val] * count)
|
||||
continue
|
||||
|
||||
try:
|
||||
# 处理 Fortran 科学计数法
|
||||
# 处理 "- 14.2" 这种中间有空格的负数
|
||||
val = part.replace('d', 'e').replace('D', 'e')
|
||||
# 移除负号和数字之间的空格
|
||||
val = re.sub(r'-\s+(\d)', r'-\1', val)
|
||||
# 移除科学计数法中的多余空格 (如 "1.48 e-2" -> "1.48e-2")
|
||||
val = re.sub(r'(\d)\s+([eEdD])', r'\1\2', val)
|
||||
values.append(float(val))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def generate_data_rs(all_arrays: dict[str, list[dict]]) -> str:
|
||||
"""生成 src/data.rs 内容"""
|
||||
lines = []
|
||||
lines.append("//! Fortran 数据数组自动导出")
|
||||
lines.append("//!")
|
||||
lines.append("//! 由 extract_fortran_data.py 自动生成,请勿手动修改")
|
||||
lines.append("")
|
||||
|
||||
# 收集已使用的名称,避免重复
|
||||
used_names = set()
|
||||
|
||||
# 按源文件分组
|
||||
for source, arrays in sorted(all_arrays.items()):
|
||||
# 过滤有数据的数组
|
||||
valid_arrays = [a for a in arrays if a.get("data") and len(a["data"]) > 0]
|
||||
if not valid_arrays:
|
||||
continue
|
||||
|
||||
lines.append(f"// ========== {source} ==========")
|
||||
lines.append("")
|
||||
|
||||
for arr in valid_arrays:
|
||||
base_name = arr["name"].upper()
|
||||
# 清理名称中的特殊字符
|
||||
base_name = re.sub(r'[^A-Z0-9_]', '', base_name)
|
||||
|
||||
# 所有变量都添加文件名前缀,避免命名冲突
|
||||
prefix = Path(source).stem.upper()[:8] # 取文件名前8个字符
|
||||
name = f"{prefix}_{base_name}"
|
||||
|
||||
# 如果加上前缀后仍有冲突,添加序号
|
||||
if name in used_names:
|
||||
counter = 1
|
||||
while f"{name}_{counter}" in used_names:
|
||||
counter += 1
|
||||
name = f"{name}_{counter}"
|
||||
|
||||
used_names.add(name)
|
||||
|
||||
dims = arr["dims"]
|
||||
data = arr["data"]
|
||||
|
||||
if not data:
|
||||
continue
|
||||
|
||||
total_size = len(data)
|
||||
|
||||
# 跳过单值 parameter
|
||||
if arr.get("is_param") and total_size == 1:
|
||||
lines.append(f"/// {arr['name']} (from {source})")
|
||||
lines.append(f"pub const {name}: f64 = {data[0]};")
|
||||
lines.append("")
|
||||
continue
|
||||
|
||||
if len(dims) == 0:
|
||||
# 未知维度,用 Vec 格式输出以便检查
|
||||
lines.append(f"/// {arr['name']} (from {source}, 未知维度,共 {len(data)} 个值)")
|
||||
lines.append(f"pub const {name}: [f64; {len(data)}] = [")
|
||||
for i, val in enumerate(data):
|
||||
if i % 10 == 0:
|
||||
lines.append(" ")
|
||||
lines[-1] += f"{val},"
|
||||
lines.append("];")
|
||||
lines.append("")
|
||||
|
||||
elif len(dims) == 1:
|
||||
# 1D 数组 - 检查数据量是否匹配
|
||||
expected_size = dims[0]
|
||||
if len(data) != expected_size:
|
||||
print(f"警告: {name} 期望 {expected_size} 个值,实际 {len(data)} 个,跳过")
|
||||
continue
|
||||
|
||||
lines.append(f"/// {arr['name']}({dims[0]}) from {source}")
|
||||
lines.append(f"pub const {name}: [f64; {dims[0]}] = [")
|
||||
for i, val in enumerate(data):
|
||||
if i % 10 == 0:
|
||||
lines.append(" ")
|
||||
lines[-1] += f"{val},"
|
||||
lines.append("];")
|
||||
lines.append("")
|
||||
|
||||
elif len(dims) == 2:
|
||||
# 2D 数组 - 直接转换为 Rust 行优先格式
|
||||
nj, ni = dims[0], dims[1]
|
||||
expected_size = nj * ni
|
||||
|
||||
if len(data) < expected_size:
|
||||
print(f"警告: {name} 期望 {expected_size} 个值,实际 {len(data)} 个,跳过")
|
||||
continue
|
||||
|
||||
lines.append(f"/// {arr['name']}({nj}, {ni}) from {source}")
|
||||
lines.append(f"/// 已转换为 Rust 行优先格式")
|
||||
lines.append(f"pub const {name}: [[f64; {ni}]; {nj}] = [")
|
||||
|
||||
# 列优先 → 行优先 转换
|
||||
for j in range(nj):
|
||||
row = []
|
||||
for i in range(ni):
|
||||
idx = j + i * nj # Fortran 列优先索引
|
||||
row.append(str(data[idx]))
|
||||
lines.append(f" [{','.join(row)}],")
|
||||
|
||||
lines.append("];")
|
||||
lines.append("")
|
||||
|
||||
# 不再需要转换函数和 getter,2D 数组直接生成为 const
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def parse_include_files(extracted_dir: Path) -> dict:
|
||||
"""解析 .FOR include 文件中的全局参数"""
|
||||
global_params = {}
|
||||
|
||||
# 扫描 .FOR 文件
|
||||
for for_file in extracted_dir.glob("*.FOR"):
|
||||
try:
|
||||
content = for_file.read_text()
|
||||
except:
|
||||
# 也尝试 tlusty/ 根目录
|
||||
for_file = Path("tlusty") / for_file.name
|
||||
if for_file.exists():
|
||||
content = for_file.read_text()
|
||||
else:
|
||||
continue
|
||||
|
||||
# 清理注释
|
||||
lines = []
|
||||
for line in content.split('\n'):
|
||||
if '!' in line:
|
||||
line = line.split('!')[0]
|
||||
lines.append(line)
|
||||
content = '\n'.join(lines)
|
||||
|
||||
# 解析 parameter 语句
|
||||
param_pattern = r'parameter\s*\(([^)]+)\)'
|
||||
for match in re.finditer(param_pattern, content, re.IGNORECASE):
|
||||
params_str = match.group(1)
|
||||
for param in params_str.split(','):
|
||||
param = param.strip()
|
||||
if '=' in param:
|
||||
name, val = param.split('=', 1)
|
||||
name = name.strip().lower()
|
||||
val = val.strip().lower()
|
||||
try:
|
||||
if '.' not in val and 'e' not in val and 'd' not in val:
|
||||
global_params[name] = int(val)
|
||||
else:
|
||||
val = val.replace('d', 'e')
|
||||
global_params[name] = int(float(val))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return global_params
|
||||
|
||||
|
||||
def main():
|
||||
# 扫描 tlusty/extracted 目录
|
||||
extracted_dir = Path("tlusty/extracted")
|
||||
|
||||
if not extracted_dir.exists():
|
||||
print(f"错误: 目录不存在: {extracted_dir}")
|
||||
return
|
||||
|
||||
# 首先解析 include 文件中的全局参数
|
||||
global_params = parse_include_files(extracted_dir)
|
||||
print(f"从 .FOR include 文件中提取了 {len(global_params)} 个全局参数")
|
||||
|
||||
all_arrays = {}
|
||||
|
||||
# 扫描所有 .f 文件
|
||||
for fortran_file in sorted(extracted_dir.glob("*.f")):
|
||||
arrays = parse_fortran_file(fortran_file, global_params)
|
||||
if arrays:
|
||||
all_arrays[fortran_file.name] = arrays
|
||||
print(f"解析: {fortran_file.name} -> {len(arrays)} 个数组")
|
||||
|
||||
# 统计
|
||||
total_arrays = sum(len(arrs) for arrs in all_arrays.values())
|
||||
arrays_with_data = sum(
|
||||
1 for arrs in all_arrays.values()
|
||||
for a in arrs if a.get("data") and len(a["data"]) > 0
|
||||
)
|
||||
arrays_2d = sum(
|
||||
1 for arrs in all_arrays.values()
|
||||
for a in arrs if len(a.get("dims", [])) == 2 and a.get("data")
|
||||
)
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f"总计: {total_arrays} 个数组, {arrays_with_data} 个有数据, {arrays_2d} 个 2D 数组")
|
||||
print("=" * 60)
|
||||
|
||||
# 生成 data.rs
|
||||
output_path = Path("src/data.rs")
|
||||
rust_code = generate_data_rs(all_arrays)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(rust_code)
|
||||
|
||||
print(f"已生成: {output_path}")
|
||||
print()
|
||||
print("在 lib.rs 或 main.rs 中添加:")
|
||||
print(" pub mod data;")
|
||||
print()
|
||||
print("使用方法:")
|
||||
print(" use crate::data::{TT, PN, get_p4b};")
|
||||
print(" let p4b = get_p4b(); // 自动初始化并返回 2D 数组")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,205 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将 Fortran DATA 语句转换为 Rust 2D 数组
|
||||
|
||||
用法:
|
||||
python3 scripts/fortran_to_rust_array.py tlusty/extracted/pffe.f
|
||||
|
||||
输出: Rust 代码片段,可直接复制到 .rs 文件中
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def parse_fortran_arrays(filepath: str) -> list[dict]:
|
||||
"""
|
||||
解析 Fortran 文件中的数组定义和 DATA 语句
|
||||
|
||||
返回: [{"name": "p4a", "dims": [22], "data": [...]}, ...]
|
||||
"""
|
||||
with open(filepath, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
arrays = {}
|
||||
|
||||
# 1. 解析 dimension 语句
|
||||
# dimension p4a(22), p4b(10,28), ...
|
||||
dim_pattern = r'dimension\s+([a-z0-9_,()\s]+)'
|
||||
for match in re.finditer(dim_pattern, content, re.IGNORECASE):
|
||||
dim_str = match.group(1)
|
||||
# 解析每个数组
|
||||
arr_pattern = r'(\w+)\s*\(([^)]+)\)'
|
||||
for arr_match in re.finditer(arr_pattern, dim_str):
|
||||
name = arr_match.group(1).lower()
|
||||
dims = [int(d.strip()) for d in arr_match.group(2).split(',')]
|
||||
arrays[name] = {"name": name, "dims": dims, "data": None}
|
||||
|
||||
# 2. 解析 data 语句
|
||||
# data p4a / val1, val2, ... /
|
||||
# 或多行:
|
||||
# data p4b /
|
||||
# * val1, val2, ...,
|
||||
# * val3, ... /
|
||||
|
||||
# 先找到所有 data 块
|
||||
data_pattern = r'data\s+(\w+)\s*/\s*([^/]+)\s*/'
|
||||
for match in re.finditer(data_pattern, content, re.IGNORECASE | re.DOTALL):
|
||||
name = match.group(1).lower()
|
||||
data_str = match.group(2)
|
||||
|
||||
if name not in arrays:
|
||||
arrays[name] = {"name": name, "dims": [], "data": None}
|
||||
|
||||
# 解析数值
|
||||
values = parse_data_values(data_str)
|
||||
arrays[name]["data"] = values
|
||||
|
||||
return list(arrays.values())
|
||||
|
||||
|
||||
def parse_data_values(data_str: str) -> list[float]:
|
||||
"""解析 DATA 语句中的数值,处理重复语法如 7*1.387"""
|
||||
values = []
|
||||
|
||||
# 清理: 移除注释、换行,但保留 * 用于重复语法
|
||||
data_str = re.sub(r'[cC]\s*$', '', data_str) # 行尾注释
|
||||
# 移除 Fortran 续行符 * (行首的 *),但保留数据中的 *
|
||||
lines = data_str.split('\n')
|
||||
cleaned_lines = []
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('*'):
|
||||
line = line[1:].strip()
|
||||
cleaned_lines.append(line)
|
||||
data_str = ' '.join(cleaned_lines)
|
||||
|
||||
for part in data_str.split(','):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
|
||||
# 处理重复语法: "7*1.387" 或 "7*1.387"
|
||||
if '*' in part and not part.startswith('-'): # 避免把负数当重复
|
||||
# 检查是否真的是重复语法
|
||||
match = re.match(r'(\d+)\s*\*\s*(-?[\d.]+)', part)
|
||||
if match:
|
||||
count = int(match.group(1))
|
||||
val = float(match.group(2))
|
||||
values.extend([val] * count)
|
||||
continue
|
||||
|
||||
try:
|
||||
values.append(float(part))
|
||||
except ValueError:
|
||||
# 跳过无法解析的部分
|
||||
pass
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def generate_rust_code(arrays: list[dict]) -> str:
|
||||
"""生成 Rust 代码"""
|
||||
lines = []
|
||||
lines.append("// 自动生成的数组数据")
|
||||
lines.append("")
|
||||
|
||||
for arr in arrays:
|
||||
name = arr["name"].upper()
|
||||
dims = arr["dims"]
|
||||
data = arr["data"]
|
||||
|
||||
if not data:
|
||||
continue
|
||||
|
||||
total_size = len(data)
|
||||
|
||||
if len(dims) == 1:
|
||||
# 1D 数组
|
||||
lines.append(f"const {name}_RAW: [f64; {total_size}] = [")
|
||||
for i, val in enumerate(data):
|
||||
if i % 10 == 0:
|
||||
lines.append(" ",)
|
||||
lines[-1] += f"{val},"
|
||||
lines.append("];")
|
||||
lines.append(f"static {name}: OnceLock<[f64; {dims[0]}]> = OnceLock::new();")
|
||||
lines.append("")
|
||||
|
||||
elif len(dims) == 2:
|
||||
# 2D 数组
|
||||
nj, ni = dims[0], dims[1]
|
||||
lines.append(f"// {name}: Fortran {name.lower()}({nj}, {ni})")
|
||||
lines.append(f"const {name}_RAW: [f64; {total_size}] = [")
|
||||
for i, val in enumerate(data):
|
||||
if i % 10 == 0:
|
||||
lines.append(" ")
|
||||
lines[-1] += f"{val},"
|
||||
lines.append("];")
|
||||
lines.append(f"static {name}: OnceLock<[[f64; {ni}]; {nj}]> = OnceLock::new();")
|
||||
lines.append("")
|
||||
|
||||
# 添加转换函数
|
||||
lines.append("")
|
||||
lines.append("/// Fortran 列优先 → Rust 行优先")
|
||||
lines.append("const fn fortran_to_rust_2d<const NJ: usize, const NI: usize>(")
|
||||
lines.append(" data: &[f64; NJ * NI],")
|
||||
lines.append(") -> [[f64; NI]; NJ] {")
|
||||
lines.append(" let mut result = [[0.0; NI]; NJ];")
|
||||
lines.append(" let mut i = 0;")
|
||||
lines.append(" while i < NI {")
|
||||
lines.append(" let mut j = 0;")
|
||||
lines.append(" while j < NJ {")
|
||||
lines.append(" result[j][i] = data[j + i * NJ];")
|
||||
lines.append(" j += 1;")
|
||||
lines.append(" }")
|
||||
lines.append(" i += 1;")
|
||||
lines.append(" }")
|
||||
lines.append(" result")
|
||||
lines.append("}")
|
||||
lines.append("")
|
||||
|
||||
# 添加初始化代码
|
||||
lines.append("// 初始化函数中调用:")
|
||||
for arr in arrays:
|
||||
name = arr["name"].upper()
|
||||
dims = arr["dims"]
|
||||
if len(dims) == 2 and arr["data"]:
|
||||
nj, ni = dims[0], dims[1]
|
||||
lines.append(f"let {name.lower()} = {name}.get_or_init(|| fortran_to_rust_2d::<{nj}, {ni}>(&{name}_RAW));")
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("用法: python3 scripts/fortran_to_rust_array.py <fortran_file>")
|
||||
print("示例: python3 scripts/fortran_to_rust_array.py tlusty/extracted/pffe.f")
|
||||
sys.exit(1)
|
||||
|
||||
filepath = sys.argv[1]
|
||||
|
||||
if not Path(filepath).exists():
|
||||
print(f"错误: 文件不存在: {filepath}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"解析: {filepath}")
|
||||
print("=" * 60)
|
||||
|
||||
arrays = parse_fortran_arrays(filepath)
|
||||
|
||||
print(f"找到 {len(arrays)} 个数组:")
|
||||
for arr in arrays:
|
||||
dims_str = ', '.join(str(d) for d in arr['dims'])
|
||||
data_count = len(arr['data']) if arr['data'] else 0
|
||||
print(f" - {arr['name']}({dims_str}): {data_count} 个值")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("生成的 Rust 代码:")
|
||||
print("=" * 60)
|
||||
print(generate_rust_code(arrays))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成 Fortran 重构追踪 Markdown 文档。
|
||||
|
||||
用法: python3 generate_tracking.py > FORTRAN_TRACKING.md
|
||||
"""
|
||||
|
||||
import csv
|
||||
import os
|
||||
|
||||
def main():
|
||||
csv_path = "/home/fmq/program/tlusty/tl208-s54/rust/fortran_analysis.csv"
|
||||
|
||||
with open(csv_path, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
|
||||
# 统计
|
||||
total = len(rows)
|
||||
done = sum(1 for r in rows if r['status'] == 'done')
|
||||
pending = sum(1 for r in rows if r['status'] == 'pending')
|
||||
pure = sum(1 for r in rows if r['is_pure'] == 'True')
|
||||
has_io = sum(1 for r in rows if r['has_io'] == 'True')
|
||||
|
||||
# 打印 Markdown 头
|
||||
print("""# Fortran 重构追踪表
|
||||
|
||||
> 自动生成,请勿手动修改。运行 `python3 scripts/generate_tracking.py > FORTRAN_TRACKING.md` 更新。
|
||||
|
||||
## 统计
|
||||
|
||||
| 指标 | 数量 |
|
||||
|------|------|
|
||||
| 总单元数 | {total} |
|
||||
| 已完成 | {done} |
|
||||
| 待处理 | {pending} |
|
||||
| 纯函数 | {pure} |
|
||||
| 有文件 I/O | {has_io} |
|
||||
| 完成率 | {rate:.1f}% |
|
||||
|
||||
## 状态说明
|
||||
|
||||
- ✅ `done` - 已重构为 Rust
|
||||
- ⬜ `pending` - 待处理
|
||||
- 🔄 `in_progress` - 进行中
|
||||
- ⏭️ `skip` - 跳过 (I/O 依赖或暂不处理)
|
||||
|
||||
## 类型说明
|
||||
|
||||
- **纯函数**: 无 COMMON 依赖、无文件 I/O、无外部调用依赖
|
||||
- **COMMON 依赖**: 需要状态结构体
|
||||
- **调用依赖**: 调用其他子程序,需要先实现依赖
|
||||
|
||||
## 完整追踪表
|
||||
|
||||
""".format(total=total, done=done, pending=pending, pure=pure, has_io=has_io, rate=100*done/total))
|
||||
|
||||
# 表格头
|
||||
print("| Fortran 文件 | 单元名 | 类型 | 纯函数 | COMMON 依赖 | 调用依赖 | I/O | Rust 模块 | 状态 |")
|
||||
print("|-------------|--------|------|--------|-------------|----------|-----|-----------|------|")
|
||||
|
||||
for r in rows:
|
||||
# 状态图标
|
||||
status_icon = "✅" if r['status'] == 'done' else "⬜"
|
||||
|
||||
# 纯函数标记
|
||||
pure_mark = "✓" if r['is_pure'] == 'True' else ""
|
||||
|
||||
# I/O 标记
|
||||
io_mark = "📁" if r['has_io'] == 'True' else ""
|
||||
|
||||
# 依赖显示
|
||||
common_deps = r['common_deps'].replace('|', ', ') if r['common_deps'] else ""
|
||||
call_deps = r['call_deps'].replace('|', ', ') if r['call_deps'] else ""
|
||||
|
||||
# Rust 模块链接
|
||||
rust_mod = r['rust_module'].replace('src/math/', '') if r['rust_module'] else ""
|
||||
|
||||
print(f"| {r['fortran_file']} | {r['unit_name']} | {r['unit_type']} | {pure_mark} | {common_deps} | {call_deps} | {io_mark} | {rust_mod} | {status_icon} |")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user