feat(parser): 支持 Viper 软关键字 fn/fun/func/function 展开为 def
行首缩进+软关键字+空白+标识符形态的函数定义(fn foo(): / function bar()) 展开为 def,行号不变,供 ast.parse 使用;变量名/调用/注解中的 fn 不受影响。 Converter 同步改为 stub 语法错误直接终止(fail-fast),不再生成残缺 vpi 被全局缓存命中后以 undefined symbol 形式爆发。
This commit is contained in:
1 parent
6a672037c2
commit
a99f126cfa
6 files changed
+48
-11
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)"""
|
||||
|
||||
@@ -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 语句
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user