# TransPyC 入口程序 import sys import os import ast import json import struct # 添加lib目录到Python路径 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib', 'includes')) from lib.includes import c from lib.includes import t from lib.constants.config import ( DEFAULT_INPUT_FILE, DEFAULT_OUTPUT_FILE, DEFAULT_COMPILE_COMMAND, DEFAULT_COMPILE_FLAGS, ERROR_MESSAGES, HELP_MESSAGE, DEFAULT_METADATA ) from lib.utils.helpers import ( DetectFileType, ExecuteCommand, AppendFileContent ) from lib.core.translator import Translator def SerializeSymbolTable(SymbolTable: dict) -> bytes: """将符号表序列化为二进制格式 格式: - 4字节: JSON数据长度(小端序) - N字节: JSON数据(UTF-8编码) Args: SymbolTable: 符号表字典 Returns: 二进制字节数据 """ JsonData = json.dumps(SymbolTable, ensure_ascii=False).encode('utf-8') length = len(JsonData) # 使用4字节小端序整数表示长度 header = struct.pack(' dict: """从二进制数据反序列化符号表 Args: data: 二进制字节数据 Returns: 符号表字典 """ if len(data) < 4: raise ValueError("Invalid symbin file: data too short") # 解析4字节长度头(小端序) length = struct.unpack(' -o [-debug ] if I + 1 < len(sys.argv): InputFile = sys.argv[I + 1] I += 2 # 解析可选参数 OutputFile = None DebugFile = None while I < len(sys.argv) and sys.argv[I].startswith('-'): if sys.argv[I] == '-o': if I + 1 < len(sys.argv): OutputFile = sys.argv[I + 1] I += 2 else: print(f'Error: -o requires an argument') sys.exit(1) elif sys.argv[I] == '-debug': if I + 1 < len(sys.argv): DebugFile = sys.argv[I + 1] I += 2 else: print(f'Error: -debug requires an argument') sys.exit(1) else: break # 如果没有指定输出文件,使用默认名称 if not OutputFile: OutputFile = InputFile.replace('.py', '.symbin').replace('.c', '.symbin') # 执行预处理 self.Args['PreSym'] = { 'input': InputFile, 'output': OutputFile, 'debug': DebugFile } else: print(f'Error: -presym requires an argument') sys.exit(1) elif sys.argv[I] == '-e' or sys.argv[I] == '--encoding': if I + 1 < len(sys.argv): self.Args['Encoding'] = sys.argv[I + 1] I += 2 else: print(f'Error: -e/--encoding requires an argument') sys.exit(1) elif sys.argv[I] == '--triple': if I + 1 < len(sys.argv): self.triple = sys.argv[I + 1] I += 2 else: print(f'Error: --triple requires an argument') sys.exit(1) elif sys.argv[I] == '--datalayout': if I + 1 < len(sys.argv): self.datalayout = sys.argv[I + 1] I += 2 else: print(f'Error: --datalayout requires an argument') sys.exit(1) else: print(f'Error: Unknown argument {sys.argv[I]}') sys.exit(1) # 检查是否有预处理符号文件的请求,如果有则跳过输入输出检查 if 'PreSym' not in self.Args and ('Input' not in self.Args or 'Output' not in self.Args): print(ERROR_MESSAGES['MISSING_ARGS']) print(HELP_MESSAGE) sys.exit(1) def CompileAndRun(self, OutputFile): """编译并运行生成的C代码""" # 获取编译命令 CompileCmd = self.Args.get('CompileCommand', DEFAULT_COMPILE_COMMAND) CompileFlags = self.Args.get('CompileFlags', DEFAULT_COMPILE_FLAGS) # 构建编译命令 FullCompileCmd = f'{CompileCmd} {CompileFlags} {OutputFile} -o {OutputFile}.exe' print(f'Compiling: {FullCompileCmd}') # 执行编译命令 try: CompileResult = ExecuteCommand(FullCompileCmd) if CompileResult: print('Compilation successful!') # 检查是否需要运行 if self.Args.get('Run', False): RunArgs = self.Args.get('RunArgs', '') RunCmd = f'{OutputFile}.exe {RunArgs}'.strip() print(f'Running: {RunCmd}') # 执行运行命令 RunResult = ExecuteCommand(RunCmd) if RunResult: print('Execution output:') print(RunResult.stdout) if RunResult.stderr: print('Execution errors:') print(RunResult.stderr) except Exception as e: print(f'{ERROR_MESSAGES["COMPILE_FAILED"]}: {e}') def WriteDebugInfo(self, content): """写入调试信息到指定文件""" # 获取调试文件路径 if 'Debug' in self.Args: DebugFile = self.Args['Debug'] else: DebugFile = self.Args.get('Output', '').replace('.c', '.p2c') if DebugFile: AppendFileContent(DebugFile, content) def PythonToC(self, InputFile, OutputFile): """Python到C的转换""" # 获取编码参数 encoding = self.Args.get('Encoding', 'utf-8') # 设置调试文件路径(使用 .p2c 扩展名) if 'Debug' in self.Args: DebugFile = self.Args['Debug'] else: # 默认使用 .p2c 文件 DebugFile = OutputFile.replace('.c', '.p2c') # 设置翻译器的调试文件 self.translator.SetDebugFile(DebugFile) # 先清空调试文件 with open(DebugFile, 'w', encoding=encoding) as f: f.write('') # 解析辅助文件,提取符号信息 if self.HelperFiles: self.translator.ParseHelperFiles(self.HelperFiles, encoding) # 加载注解文件,提取类型定义 if self.AnnotationFiles: self.translator.LoadAnnotationFiles(self.AnnotationFiles) # 获取编码参数 encoding = self.Args.get('Encoding', 'utf-8') with open(InputFile, 'r', encoding=encoding) as F: Content = F.read() # 保存原始代码行 self.translator.OriginalLines = Content.split('\n') self.translator.Content = Content # 解析Python代码为AST Tree = ast.parse(Content) # 解析文件以提取类型信息(用于 EmbeddedAssignments) self.translator.ParsePythonFile(InputFile, encoding) # 写入AST树信息(压缩格式) with open(DebugFile, 'a', encoding=encoding) as f: f.write('=== AST Tree (Compact) ===\n') f.write(ast.dump(Tree)) f.write('\n\n') Target = 'llvm' CCode = self.translator.GenerateCCode(Tree, target=Target) import datetime timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') filename = os.path.basename(OutputFile) encoding = self.Args.get('Encoding', 'utf-8') with open(OutputFile, 'w', encoding=encoding) as F: F.write(CCode) F.write('\n') def CToPython(self, InputFile, OutputFile, HeaderFiles): """C到Python的转换(暂时禁用)""" print("C到Python的转换功能暂时禁用,请使用Python到C的转换功能。") def AddSymbol(self, SymbolFiles): """添加符号文件 Args: SymbolFiles: 符号文件或符号文件列表 """ if isinstance(SymbolFiles, list): self.SymbolFiles.extend(SymbolFiles) else: self.SymbolFiles.append(SymbolFiles) # 收集文件路径和编码 HelperFiles = [] encodings = [] for SymbolFile in self.SymbolFiles: FilePath = SymbolFile.FilePath encoding = SymbolFile.encoding if FilePath: HelperFiles.append(FilePath) encodings.append(encoding) # 解析辅助文件 if HelperFiles: # 暂时使用第一个文件的编码 encoding = encodings[0] if encodings else 'utf-8' self.translator.ParseHelperFiles(HelperFiles, encoding) def Convert(self, OutputFilename=None, SourceFilename=None, target='llvm'): """转换代码 Args: OutputFilename: 输出文件名(用于模板渲染) SourceFilename: 源文件路径(用于设置CurrentFile,影响导入模块的查找) target: 目标代码类型,仅支持 'llvm' Returns: 如果debug是字符串,则返回生成的代码 如果debug是True,则返回生成的代码和调试信息 """ if not self.code: print('Error: No code provided') return None from lib.core.Handles.HandlesImports import ImportHandle ImportHandle._struct_load_cache.clear() # 如果提供了 SourceFilename,先设置 CurrentFile # 这样导入模块时会相对于源文件所在目录查找 if SourceFilename: self.translator.CurrentFile = SourceFilename # 加载注解文件,提取类型定义 if self.AnnotationFiles: self.translator.LoadAnnotationFiles(self.AnnotationFiles) # 保存原始代码行 self.translator.OriginalLines = self.code.split('\n') # 解析Python代码为AST Tree = ast.parse(self.code) # 从源代码直接预扫描 import 语句,注册别名 # 注意:不能依赖 Tree 中的 import 节点,因为 ParsePythonFile 可能修改了 AST if not getattr(self.translator, '_import_aliases', None): self.translator._import_aliases = {} if not getattr(self.translator, '_imported_modules', None): self.translator._imported_modules = set() _prescan_tree = ast.parse(self.code) for _node in ast.iter_child_nodes(_prescan_tree): if isinstance(_node, ast.Import): for _alias in _node.names: _name = _alias.name if _name in ('c', 't'): continue self.translator._imported_modules.add(_name) if _alias.asname: self.translator._import_aliases[_alias.asname] = _name elif isinstance(_node, ast.ImportFrom): _module_name = _node.module if _module_name and _module_name not in ('c', 't'): self.translator._imported_modules.add(_module_name) for _alias in _node.names: if _alias.asname: self.translator._import_aliases[_alias.asname] = f"{_module_name}.{_alias.name}" # 解析代码以提取类型信息(用于 EmbeddedAssignments 和 TypedefAssignments) # 创建临时文件路径用于解析 import tempfile import os with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f: f.write(self.code) TempFile = f.name try: # UpdateCurrentFile=False 表示不修改 CurrentFile if SourceFilename: self.translator.ParsePythonFile(TempFile, UpdateCurrentFile=False) else: self.translator.ParsePythonFile(TempFile) finally: os.unlink(TempFile) import datetime timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') if self.triple: self.translator.triple = self.triple if self.datalayout: self.translator.datalayout = self.datalayout # 生成代码 code = self.translator.GenerateCCode(Tree, target='llvm') filename = os.path.basename(OutputFilename) if OutputFilename else 'generated.ll' # 处理调试信息 if isinstance(self.config.debug, str): # 如果debug是字符串,写入调试信息到文件 with open(self.config.debug, 'w', encoding='utf-8') as f: f.write('=== Symbol Table ===\n') f.write(str(self.translator.SymbolTable) + '\n\n') f.write('=== AST Tree ===\n') f.write(ast.dump(Tree, indent=2) + '\n\n') f.write('=== Export Table ===\n') f.write(self.translator.ExportTable.to_json() + '\n\n') f.write('=== Generated Code ===\n') f.write(code + '\n') return code elif self.config.debug: # 如果debug是True,返回生成的代码和调试信息 DebugInfo = { 'SymbolTable': self.translator.SymbolTable, 'ast_tree': ast.dump(Tree, indent=2), 'export_table': self.translator.ExportTable.to_dict(), 'generated_code': code } return code, DebugInfo else: # 如果debug是False,只返回生成的代码 return code def Run(self): """主运行函数""" # 检查是否有命令行参数 if len(sys.argv) > 1: # 有命令行参数,使用 ParseArgs 方法解析 self.ParseArgs() # 检查是否有预处理符号文件的请求 if 'PreSym' in self.Args: PresymConfig = self.Args['PreSym'] InputFile = PresymConfig['input'] OutputFile = PresymConfig['output'] DebugFile = PresymConfig['debug'] # 获取编码参数 encoding = self.Args.get('Encoding', 'utf-8') # 推断文件类型 FileType = 'py' if InputFile.endswith('.py') else 'c' if InputFile.endswith('.c') else None # 创建 SymbolFile 对象,传入编码参数 SymbolFile = SymbolFile(file=InputFile, type=FileType, encoding=encoding) # 调用 PreProcessSymbol if DebugFile: BinaryData, DebugInfo = TransPyC.PreProcessSymbol(SymbolFile, debug=True) # 写入调试文件 with open(DebugFile, 'w', encoding=encoding) as f: f.write(DebugInfo) print(f"Debug info written to: {DebugFile}") else: BinaryData = TransPyC.PreProcessSymbol(SymbolFile, debug=False) # 写入二进制符号文件 with open(OutputFile, 'wb') as f: f.write(BinaryData) print(f"Symbol file generated: {OutputFile}") return InputFile = self.Args['Input'] OutputFile = self.Args['Output'] else: # 没有命令行参数,使用默认的测试文件名称 InputFile = DEFAULT_INPUT_FILE OutputFile = DEFAULT_OUTPUT_FILE print('Using default test files: test.py -> test.c') FileType = DetectFileType(InputFile) if FileType == '.py': self.PythonToC(InputFile, OutputFile) # 检查是否需要编译和运行 if 'CompileCommand' in self.Args or self.Args.get('Run', False): self.CompileAndRun(OutputFile) elif FileType == '.c': self.CToPython(InputFile, OutputFile, self.HeaderFiles) else: print(f'Error: Unsupported file type {FileType}') sys.exit(1) if __name__ == '__main__': trans = TransPyC() if 'SliceLevel' in trans.Args: trans.SliceLevel = trans.Args['SliceLevel'] trans.Run()