From a99f126cfa230229826f6078fb14c9438b417020 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 13 Aug 2026 07:51:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(parser):=20=E6=94=AF=E6=8C=81=20Viper=20?= =?UTF-8?q?=E8=BD=AF=E5=85=B3=E9=94=AE=E5=AD=97=20fn/fun/func/function=20?= =?UTF-8?q?=E5=B1=95=E5=BC=80=E4=B8=BA=20def?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 行首缩进+软关键字+空白+标识符形态的函数定义(fn foo(): / function bar()) 展开为 def,行号不变,供 ast.parse 使用;变量名/调用/注解中的 fn 不受影响。 Converter 同步改为 stub 语法错误直接终止(fail-fast),不再生成残缺 vpi 被全局缓存命中后以 undefined symbol 形式爆发。 --- lib/Projectrans/Utils.py | 7 +++++-- lib/Shell/TransPyC.py | 8 ++++++++ lib/StubGen/Converter.py | 15 ++++++++------- lib/core/SymbolUtils.py | 19 +++++++++++++++++++ lib/core/Translator/AnnotationLoader.py | 5 ++++- lib/core/Translator/PythonParser.py | 5 ++++- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/lib/Projectrans/Utils.py b/lib/Projectrans/Utils.py index ff56c76..2bff1d0 100644 --- a/lib/Projectrans/Utils.py +++ b/lib/Projectrans/Utils.py @@ -6,7 +6,7 @@ import ast from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _ConfigMode -from lib.core.SymbolUtils import AnnotationContainsName +from lib.core.SymbolUtils import AnnotationContainsName, ExpandSoftKeywords def compute_sha1(content: str) -> str: @@ -59,7 +59,10 @@ def parse_python_file(filepath: str) -> tuple[str, ast.Module] | tuple[str, None except Exception: return ('', None) try: - tree: ast.Module = ast.parse(content) + # 软关键字展开:fn/fun/func/function -> def(行号不变),供 ast.parse 使用。 + # 注意 content 保持原始文本返回(compute_sha1 必须与 Phase1 一致), + # 仅 tree 基于展开后的代码解析。 + tree: ast.Module = ast.parse(ExpandSoftKeywords(content)) except SyntaxError: return (content, None) _FILE_AST_CACHE[filepath] = (mtime, content, tree) diff --git a/lib/Shell/TransPyC.py b/lib/Shell/TransPyC.py index 324d3ee..dea23a3 100644 --- a/lib/Shell/TransPyC.py +++ b/lib/Shell/TransPyC.py @@ -20,6 +20,7 @@ from lib.utils.helpers import ( DetectFileType, ExecuteCommand, AppendFileContent ) +from lib.core.SymbolUtils import ExpandSoftKeywords from lib.core.VLogger import info, warning, error, success, debug from lib.core.translator import Translator from lib.core.Handles.HandlesBase import CTypeInfo @@ -96,6 +97,7 @@ class TransPyC: if FileType == 'py': # Python文件处理 + content = ExpandSoftKeywords(content) translator.OriginalLines = content.split('\n') translator.Content = content @@ -416,6 +418,9 @@ class TransPyC: with open(InputFile, 'r', encoding=encoding) as F: Content: str = F.read() + # 软关键字展开:fn/fun/func/function -> def,供 ast.parse 使用 + Content = ExpandSoftKeywords(Content) + # 保存原始代码行 self.translator.OriginalLines = Content.split('\n') self.translator.Content = Content @@ -490,6 +495,9 @@ class TransPyC: error('未提供代码') return None + # 软关键字展开:fn/fun/func/function -> def,供 ast.parse 使用 + self.code = ExpandSoftKeywords(self.code) + from lib.core.Handles.HandlesImports import ImportHandle ImportHandle._struct_Load_cache.clear() diff --git a/lib/StubGen/Converter.py b/lib/StubGen/Converter.py index 743ea5d..d49665d 100644 --- a/lib/StubGen/Converter.py +++ b/lib/StubGen/Converter.py @@ -4,7 +4,7 @@ import ast import re import fnmatch from typing import List, Dict -from lib.core.SymbolUtils import IsListAnnotation +from lib.core.SymbolUtils import IsListAnnotation, ExpandSoftKeywords class PythonToStubConverter: @@ -53,6 +53,9 @@ class PythonToStubConverter: @staticmethod def convert(PyContent: str, ModuleName: str) -> str: """将 Python 代码转换为存根格式""" + # 软关键字展开:fn/fun/func/function -> def(行内等宽/更短,行号不变), + # 保证后续 ast.parse 与 SourceLines 回读行号一致。 + PyContent = ExpandSoftKeywords(PyContent) lines: list[str] = PyContent.split('\n') StubLines: list[str] = [] @@ -210,12 +213,10 @@ class PythonToStubConverter: #StubLines.append('c.CEndif()') except SyntaxError as e: - # 如果解析失败,添加注释说明 - StubLines.append(f'# Warning: Failed to parse Python code: {e}') - StubLines.append('# Original content:') - StubLines.append('"""') - StubLines.append(PyContent[:1000]) # 只显示前1000字符 - StubLines.append('"""') + # 语法错误直接终止(fail-fast),不允许生成残缺 stub 被全局缓存命中。 + # 残缺 vpi 只含 import 头,函数/类型声明全缺,会在后续阶段以 + # undefined symbol / mangle 错误形式爆发(如 ExpandKeywordsTest 链接失败)。 + raise return '\n'.join(StubLines) diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 8aadd76..24a2d90 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -1,10 +1,29 @@ """共享工具函数:符号表处理中使用的 AST 分析工具""" from __future__ import annotations import ast +import re from lib.includes import t _T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) +# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义, +# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。 +_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile( + r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE +) + + +def ExpandSoftKeywords(Code: str) -> str: + """将 Viper 软关键字 fn/fun/func/function 展开为 def(函数定义行)。 + + 仅在"行首缩进 + 关键字 + 空白 + 标识符"的函数定义形态匹配, + 变量名(fn = 5)、调用(fn(x))、注解(x: fn)与字符串内容不受影响。 + 展开为 def 与关键字等宽或更短,行号不变,AST 行号映射保持有效。 + """ + if not Code: + return Code + return _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code) + def IsTModule(module_id: str, symtable: object | None = None) -> bool: """检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)""" diff --git a/lib/core/Translator/AnnotationLoader.py b/lib/core/Translator/AnnotationLoader.py index 3e64d43..24a6d25 100644 --- a/lib/core/Translator/AnnotationLoader.py +++ b/lib/core/Translator/AnnotationLoader.py @@ -9,7 +9,7 @@ from typing import Any import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) from lib.core.Handles.HandlesBase import CTypeInfo -from lib.core.SymbolUtils import IsTModule, AnnotationContainsName +from lib.core.SymbolUtils import IsTModule, AnnotationContainsName, ExpandSoftKeywords from lib.includes import t from lib.includes.t import CType, CEnum from lib.constants.config import mode as _config_mode @@ -197,6 +197,7 @@ class AnnotationLoaderMixin: try: with open(ModuleName, 'r', encoding='utf-8') as f: ann_content: str = f.read() + ann_content = ExpandSoftKeywords(ann_content) ann_tree: ast.Module = ast.parse(ann_content) for node in ann_tree.body: if isinstance(node, ast.ClassDef): @@ -237,6 +238,7 @@ class AnnotationLoaderMixin: try: with open(ModuleName, 'r', encoding='utf-8') as f: ann_content: str = f.read() + ann_content = ExpandSoftKeywords(ann_content) ann_tree: ast.Module = ast.parse(ann_content) for node in ann_tree.body: # 检测 xxx: t.CEnum 形式的枚举成员变量 @@ -283,6 +285,7 @@ class AnnotationLoaderMixin: try: with open(ModuleName, 'r', encoding='utf-8') as f: ann_content: str = f.read() + ann_content = ExpandSoftKeywords(ann_content) ann_tree: ast.Module = ast.parse(ann_content) for node in ann_tree.body: # 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句 diff --git a/lib/core/Translator/PythonParser.py b/lib/core/Translator/PythonParser.py index 9bcabcc..bfe4e49 100644 --- a/lib/core/Translator/PythonParser.py +++ b/lib/core/Translator/PythonParser.py @@ -6,7 +6,7 @@ import struct from lib.utils.helpers import DetectFileType from lib.core.Handles.HandlesBase import CTypeInfo -from lib.core.SymbolUtils import IsTModule, AnnotationContainsName +from lib.core.SymbolUtils import IsTModule, AnnotationContainsName, ExpandSoftKeywords from lib.includes import t from lib.constants.config import mode as _ConfigMode from lib.core.VLogger import get_logger as _vlog @@ -72,6 +72,9 @@ class PythonParserMixin: with open(FilePath, 'r', encoding=encoding) as f: content: str = f.read() + # 软关键字展开:fn/fun/func/function -> def,供 ast.parse 使用 + content = ExpandSoftKeywords(content) + # 保存当前处理的文件路径(仅在主文件处理时更新) if UpdateCurrentFile: self.CurrentFile = FilePath